Remove element from observableArray within Subscribe function without signalling it again - javascript

What I'm trying to do is the following:
var queueManagerClass = function() {
this.queue = ko.observableArray();
this.queue.subscribe( function(theChangedQueue) {
// Do some nice things..
// Nice things are over, remove the last item from the queue..
this.queue.remove(theChangedQueue.pop());
}.bind(this));
};
Except 2 problems occur: when I call this.queue.remove(item); I'll end up in an infinite loop.. The subscribe function will call it self over and over again..
I know there is an option to 'unbind' the subscribe function temporally but I can't risk the fact that I miss a queueItem that's inserted in the mean time of unbinding and binding again.
I hope you'll understand my (not so great..) english.
Thanks for your time!

One way to work around this is to make it detectable that you are in the process of removing the item and specifically ignore the event when that happens. This can be done by using a local to store the "isRemoving" state. For example
var isRemoving = false;
this.queue.subscribe( function(theChangedQueue) {
if (isRemoving) {
return;
}
// Do some nice things..
// Nice things are over, remove the last item from the queue..
isRemoving = true;
this.queue.remove(theChangedQueue.pop());
isRemoving = false;
}.bind(this));

Related

how can I create an event handler in a loop that does not recreate itself?

I have a class member lets call it remove. so to call it I write this.remove(arg)
I am building a table of buttons that I need this function to be an event of.
so the current code is
var me = this
for (x iterations) {
button.addEventListener('click',function() {
me.remove(this) // <- document is passed via this. I need both contexts
}
obviously this is bad code on every reiteration the function is being recreated.
remove.call()
wouldn't work because i don't have access to the new this context until it's created.
is they're a better way to write this as to not recreate the function every time?
It's not very clear what you're asking, but you can avoid recreating the function in the loop:
var me = this;
function removeButton() {
me.remove(this);
}
for (/*x iterations*/) {
button.addEventListener('click', removeButton);
}

Firing eventListener only once for all the instances of my constructor?

I have a shared property across all the instances of my constructor:
function Me() { }
Me.prototype.window = {};
I'd like to update it's content on window resize, but I'd like to do it only once for each resize event, no matter how many instances of my constructor have been created.
Logically, if I define the eventListener as below, in the initialization of my instances, it will be fired multiple times
function Me() {
window.addEventListener('resize', function() {
this.window = window.outerWidth;
}.bind(this));
}
var a = new Me();
var b = new Me();
// on resize, the callback is called twice
How can I do it?
How can I do it?
Have a flag that indicates whether to bind the event handler or not. Then, in the constructor you only need to check the flag:
if (bindHandler) {
// ... bind handler
bindHandler = false;
}
How / where you store the flag is up to you.
Thought I'd put back all the answers I gave to show how OP has not provided all pertinent information up front. Also, the answer he finally came up with and marked correct was one I offered and he shot down.
First, I offered what is probably the most simple solution:
Put the following completely outside of the constructor function. When the window is resized, the shared property is updated - - for all instances.
window.addEventListener('resize', function() {
Me.prototype.window = window.outerWidth;
};
The OP then added new information that this would not be good because if no instances of Me existed, the callback would still be registered on the prototype.
I then offered this which interestingly, he marked as the answer when someone else posted it.
Your next solution would have to be to track whether there are any instances of Me before the event listener is registered. That would mean that you'd need to keep track of whether any instances exist:
// Global variabel to track if Me instances exist
var meInstances = false
var me1 = new Me();
meInstances = true;
if(meInstances){
window.addEventListener('resize', function() {
Me.prototype.window = window.outerWidth;
};
}
But, when I posted it, the response was: "you are completely making useless all the classes, constructors and stuff like that logic. There's not isolation, you are adding a lot of code for nothing and the solution is not robust." In fact, the OP then came up with his own solution that uses an array to store the instances and then the length of the array can be checked to see if there are any. I was actually going to suggest that, but went with the Boolean flag because the OP kept saying he wanted simple.
So, I offered this:
What about:
function Me() {
// This code only runs when instance is being made
// This instance is registers a callback
window.addEventListener('resize', function() {
// Callback modifies single Prototype that all instances share
Me.prototype.window = window.outerWidth;
});
}
Me.prototype.window = {};
Now, I don't fully endorse this as a good solution, but from all the constraints the OP kept adding after each suggestion, this seemed to be a last resort. But, again was rejected.
Adding here the solution I've used in the final code.
I've added a destroy method for completeness:
function Me() {
this.init();
}
Me.prototype.window = 0;
// function used to update this.window
Me.prototype.onResizeBound = null;
Me.prototype.onResize = function() {
Me.prototype.window = window.outerWidth;
};
// if there are no instances of Me, initialize the event listener
// then, add this instance to the instances list
Me.prototype.init = function() {
if (this.instances === 0) {
Me.prototype.onResizeBound = this.onResize.bind(this);
window.addEventListener('resize', Me.prototype.onResizeBound);
}
this.instances++;
};
// remove this instance to the instances list
// if there are no more instances in the list, remove the event listener
Me.prototype.destroy = function() {
this.instances--;
if (this.instances === 0) {
window.removeEventListener('resize', Me.prototype.onResizeBound);
}
};
Me.prototype.instances = 0;
Example:
var a = new Me(); // added event listener
var b = new Me(); // not added event listener since one is already set
b.destroy(); // not removed event listener since one instance is still there
a.destroy(); // removed event listener since there are not other instances

Selector to apply jQuery event to two DOM ids

I have two select menus (#id1 and #id2) that, when validated as containing a user error, should instigate some DOM changes (remove error notice) when either one of them gets interacted with.
Again:
var Heat_Check = jQuery('#id1' or '#id2').change(function() { ... });
PS. I know there's no return value from that chain.
May be you wanted to check if change is triggered from either of those select. Try below,
var Heat_Check = false;
jQuery('#id1, #id2').change(function() { Heat_Check = true; });
function heatCheck () {
if(Heat_Check) {
//Do your stuff
console.log('It is hot');
}
}
The comment from #Vega is right, but for completeness you can also do this:
heatChangeHandler = function() {
// ....
};
$('#id1').change(heatChangeHandler);
$('#id2').change(heatChangeHandler);
In general it is better to put multiple selectors in one $(), but it's worth knowing that functions can be addressed as variables, and thus referenced many times.

jQuery seems to be running multiple calls simultaneously

Sorry, but apparently I don't understand chaining enough to figure out this problem...
I'm implementing a jQuery carousel plugin (jCarouselLite) and I'm trying to add an option to 'remove' one of the carousel items (currently <div class="remove">)...
initEvents: function() {
var self = this;
// Bind jQuery event for REMOVE button click
$('.remove').live('click', function() {
// Need to save the ID that we're removing
var item = $(this).closest('li.sort');
var itemId = item.attr("id");
$(this).removeItem();
self.refreshDisplay(itemId);
});
$.fn.removeItem = (function() {
var item = this.closest('li.sort'); // could save this call by passing param
item.fadeOut("normal", function() {
$(this).remove();
});
// preserve jQuery chain
return this;
});
},
refreshDisplay(itemId) {
// ...
// redraws carousel
// ...
// itemId is used to remove from the array of Items to show (class-wide scope)
}
Since there's no clean way to 'refresh' the jCarouselLite plugin (maybe something I'll try implementing in the actual plugin later) the quick and dirty fix for this is to just regenerate the Carousel.
The issue is I'm trying to fade out the element clicked, however, it seems like the refreshDisplay() is called before the animation of fading out (and removing) the clicked item is completed. I've verified this by commenting out the self.refreshDisplay(itemId); line and it fades out and removes as expected.
So I guess there's a certain way I need to chain this? I've done a couple hours of reading on how chaining works and I thought I understood it, but apparently not.
Any and all help is appreciated, thanks!
The purpose of chaining is to allow multiple commands to share a base object, but it doesn't cause each command to wait for the previous one.
For that, you need to use a callback. Something like
initEvents: function() {
var self = this;
// Bind jQuery event for REMOVE button click
$('.remove').live('click', function() {
// Need to save the ID that we're removing
var item = $(this).closest('li.sort');
var itemId = item.attr("id");
$(this).removeItem(function() {
self.refreshDisplay(itemId);
});
});
$.fn.removeItem = (function(callback) {
var item = this.closest('li.sort'); // could save this call by passing param
item.fadeOut("normal", function() {
$(this).remove();
callback(); //now your refresh is being called after the fade.
});
// preserve jQuery chain
return this;
});
},

Best way to prevent a javascript function from executing while it already is or another one is?

I'm using jquery and what I'm doing is binding the toggle method to a number of buttons on a webpage. It looks something like this
$('.button').toggle(function(){
// first function
}, function(){
// second function
});
However, there are animation in both of those functions. So a user can click the button while the first or second function is executing. And this messes up the order of the HTML elements and may make them move to the end of the page. Because essentially what these functions do is move one element to the end on the first click, and on the other click move it back where it originally was.
Of course, it is difficult to click the button once it is moving around the page. But it's possible.
You could use a flag. Set a flag 'isAnimating' to true when an animation begins, and false when it ends. Any subsequent animation can only proceed if this value is false.
You could also possibly check to see if the :animated selector applies to the owner of the event. And base your decisions off of that.
You could use a bool as a semiphore.. Obviously, this is in no way secure, but javascript doesn't really support locking, so you could easily have deadlocks and / or race conditions with this approach, but it will work 99,9% of the times :)
Seems like you'll be happier implementing your own toggle. Toggle really only works for cases with 0 additional logic.
$('.button').click(
function () {
if( $(self).is(":animated") {
return false;
}
if($(self).is(".rolledup")) {
self.apply(roll_window_down);
} else {
self.apply(roll_window_up);
}
});
function roll_window_up() {
$(self).addClass( 'rolledup' );
// first function
}
function roll_window_down() {
$(self).removeClass( 'rolledup' );
// first function
}
You need to place the two functions you pass to toggle in a context in which you can hold a flag to control function entrance:-
(function() {
var toggling = false;
$('.button').toggle(function(){
if (!toggling) {
toggling = true;
// first function
toggling = false;
} else {
// whatever you want to happen if re-entrance attempted
}
}, function(){
if (!toggling) {
toggling = true;
// second function
toggling = false;
} else {
// whatever you want to happen if re-entrance attempted
}
})
)();
N.B. This serialises all toggles of elements that have the .button class. IOW there is only one toggling flag for all buttons. If you want each button to have its own toggling flag:-
$('.button').each(function() {
var toggling = false;
$(this).toggle(function(){
if (!toggling) {
toggling = true;
// first function
toggling = false;
} else {
// whatever you want to happen if re-entrance attempted
}
}, function(){
if (!toggling) {
toggling = true;
// second function
toggling = false;
} else {
// whatever you want to happen if re-entrance attempted
}
});
);
You need a queue. You can build one with a semaphore variable, but jQuery already provides one, so maybe you want to use it:
$('.button').toggle(function() {
$(document).queue("foo", function() {
...
});
}, function() {
$(document).queue("foo", function() {
...
});
});
jQuery normally uses the "fx" queue to serialize animations, but you can use this "foo" queue for whatever you want.
The queue can be put on any object, so maybe you want to put it on the container that has all the .button objects in it. You cannot put it on the button (this) themselves, or you'll be back to where you're at now.
Once you've done that, all you really need to do is abort an animation. This can be done by expressly emptying the "fx" queue, or you can use $('.button').stop(); to stop all the old animations.

Categories