问题
Im stuck here, any hint would be nice.
I have an array of Objects objects[] and an array of divnames[]. My object has functions like play() and stop() on it. The Object and its' functions were tested in multiple situations, they are working. Now im Trying to iterate over the divnames[] and assign actions of the appropriate objects[] to mouseover, mouseout and click.
There was a closure problem, that i fixed with a solution that i found in another thread here on StackOverflow. That works. But the problem remaining is that the mouseover etc. actions are not assigned to divs that are loaded later on. They are working on objects that are on the page from the beginning.
Here is what i have:
$(function(){
for (var i=0, len=divnames.length; i<len; i++) {
if(divnames[i]){
(function( ) { // anonymous function to fix closures
var index = i; // also needed to fix closures
$('#'+divnames[index]).on("click", function() {
objects[index].play();
loadContent(divnames[index]+".php");
});
})( ); // direct function execution to fix closures
}
}
});
As stated above, closure problem is fixed by the two commented lines, leaving them away will only ever apply the last execution of the for-loop. As it is now, that works.
But what doesnt work, is that the click function should also be applied to divs matching the selector, but are not yet loaded.
But that does work if the code with the same functionality is not iterated inside a for loop without the closure-fix, because thats expected .on() behaviour if i understand correctly.
So how do i get both desired functionalities to work?
Thanks for your time in advance.
----edit----
Additional information as requested:
var divnames = [];
divnames[0] = "home";
divnames[1] = "about";
divnames[2] = "projects";
function SpriteAnim (options) {
var timerId = 0;
var i = 0;
this.status = 0;
this.init = function () {
var element = document.getElementById(options.elementId);
element.style.width = options.width + "px";
element.style.height = options.height + "px";
element.style.backgroundRepeat = "no-repeat";
element.style.backgroundImage = "url(" + options.sprite + ")";
};
this.showFrame = function (which) {
if (which < options.frames) {
i = which;
element = document.getElementById(options.elementId);
element.style.backgroundPosition = "0px -" + which * options.height + "px";
}
};
this.play = function () {
this.status = 2;
timerId = setInterval(function () {
if (i < (options.frames - 1)) {
i++;
element = document.getElementById(options.elementId);
element.style.backgroundPosition = "0px -" + i * options.height + "px";
} else {
clearInterval(timerId);
this.status = 1;
}
}, 100);
};
}
As you probably have already guessed, the objects[] array contains 3 SpriteAnim objects in objects[0], objects[1], objects[2].
objects[0] = new SpriteAnim({
width: 7,
height: 7,
frames: 8,
sprite: "myanim1.png",
elementId: "anim0"
});
objects[1] = new SpriteAnim({
width: 7,
height: 7,
frames: 8,
sprite: "myanim1.png",
elementId: "anim1"
});
objects[2] = new SpriteAnim({
width: 7,
height: 7,
frames: 8,
sprite: "myanim2.png",
elementId: "anim2"
});
回答1:
It's because index is declared outside of your binding statement. When the click goes into that call, it has no idea what objects[index] is.
If you want to keep the same structure, rework your function like this:
$(function(){
for (var i=0, len=divnames.length; i<len; i++) {
if(divnames[i]){
(function( ) { // anonymous function to fix closures
var index = i; // also needed to fix closures
$('#'+divnames[index]).on("click", function(e) {
switch ($(e.id).attr('name')){
case 'home':
objects[0].play();
loadContent('home.php');
break;
case 'about':
// do same
case 'projects':
// do same
default:
break;
}
});
})( ); // direct function execution to fix closures
}
}
});
Realistically, you should do this:
$(document).on('click','div[name=home],div[name=projects],div[name=about]', function(){
var name = $(this).attr('name');
switch (name){
case 'home':
objects[0].play();
break;
// and so on
}
loadContent(name + '.php');
});
EDIT:
When you click your div, this is all it will be aware of:
objects[index].play();
loadContent(divnames[index]+".php");
回答2:
You need to use event delegation, and listen for these events on a parent element rather than on the elements themselves. This way, if you add new elements later on, you don't need to re-bind to them.
$("#buttons").on("click", ".button", function(){
console.log("You clicked on a .button element.");
});
In this case, we bind to the click event on #buttons
, but only when the invoking element matches the selector .button
. Your markup would be something along the lines of this:
<div id="buttons">
<a class="button">Foo</a>
<a class="button">Bar</a>
</div>
When a click occurs on any of the .button
elements, and bubbles up to the #buttons
parent, it will be intercepted and the handler will be fired.
来源:https://stackoverflow.com/questions/13106024/jquery-click-function-bind-in-for-loop-with-closure-fix