jQuery temporary unbinding events - javascript

Maybe I'm totally missing something about even handling in jQuery, but here's my problem.
Let's assume there are some event binding, like
$(element).bind("mousemove", somefunc);
Now, I'd like to introduce a new mousemove binding that doesn't override the previous one, but temporarily exclude (unbind) it. In other words, when I bind my function, I must be sure that no other functions will ever execute for that event, until I restore them.
I'm looking for something like:
$(element).bind("mousemove", somefunc);
// Somefunc is used regularly
var savedBinding = $(element).getCurrentBinding("mousemove");
$(element).unbind("mousemove").bind("mousemove", myfunc);
// Use myfunc instead
$(element).unbind("mousemove", myfunc).bind("mousemove", savedBindings);
Of course, the somefunc is not under my control, or this would be useless :)
Is my understanding that is possible to bind multiple functions to the same event, and that the execution of those functions can't be pre-determined.
I'm aware of stopping event propagation and immediate event propagation, but I'm thinking that they are useless in my case, as the execution order can't be determined (but maybe I'm getting these wrong).
How can I do that?
EDIT: I need to highlight this: I need that the previously installed handler (somefunc) isn't executed. I am NOT defining that handler, it may be or may be not present, but its installed by a third-party user.
EDIT2: Ok, this is not feasible right now, I think I'm needing the eventListenerList, which is not implemented in most browsers yet. http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/changes.html

Another way could be to use custom events, something along these lines:
var flag = 0;
$(element).bind("mousemove", function() {
if(flag) {
$(this).trigger("supermousemove");
} else {
$(this).trigger("magicmousemove");
}
}).bind("supermousemove", function() {
// do something super
}).bind("magicmousemove", function() {
// do something magical
});
$("#foo").click(function() {
flag = flag == 1 ? 0 : 1; // simple switch
});
Highly annoying demo here: http://jsfiddle.net/SkFvW/

Good if the event is bound to multiple elements:
$('.foo').click(function() {
if ( ! $(this).hasClass('flag')) {
do something
}
});
(add class 'flag' to sort of unbind, add it to 'bind')

Related

Is it possible to bind to events on a child window from a parent?

Is it possible to bind functions to events on child windows?
document.getElementById('foo').onclick = function() {
var newWindow= window.open('other.html', "_blank");
newWindow.document.addEventListener("onreadystatechange", function(){
console.log('foo'); // This is never run. Can I construct the new window so that it is run "onreadystatechange"?
});
return false;
};
Note that I would like to bind an event to onreadystatechange. I wish to avoid a race condition, can I create a window, bind the events and then load the URL to avoid the race condition?
.addEventListener("onreadystatechange", ...)
Event properties start with "on". The event names on the other hand do not. I.e. it should be
.addEventListener("readystatechange", ...)
I have not tried avoiding the race condition because I know of no way to do so.
Ok, I'm not entirely sure how events and auxiliary browsing context initialization work with window.open(), the spec is quite complex there.
I'd just try setting DOM event breakpoints (chrome debugger has those) and see which events are fired in which order and then check if that works in other browsers.
That said, I think the simplest option here might to read the document.readyState property. If it's "complete" then the site is already fully loaded and no further state change event will be fired and you can execute your script directly instead of waiting for the event.
If you do this should be good to go.
var newwindow = window.open('other.html', "_blank");
var $ = newwindow.$; // add if needed
$(newwindow).bind('someEvent', function() { FunctionThatDoesSomethingInTheNewWindow });
return false;

How to attach an event listener to a javascript var

I am a somewhat green programmer, and quite new to javascript/jquery, but I thought I understood javascript events. Apparently not. I am not able to get event listeners to work as I'd like.
Given javascript:
var Thing = {
//stuff
update: function() {
$.event.trigger({type:'stateUpdate', more:stuff});
}
};
var Room = {
//more stuff
updateHandler: function (e) {
//handle event here
}
};
If I do jquery:
$(document).on('stateUpdate', $Room.updateHandler);
then it works fine, but I can't do either
$(Room).on('stateUpdate', $Room.updateHandler);
or
Room.addEventListerner('stateUpdate', $Room.updateHandler);
The first does nothing, the second gives .addEventListerner is not a function error.
I've googled for hours and can't figure it out. I found something that said .addEventListener only works on objects that implement EventListener, something about handleEvent, and something about functions automatically implementing EventListener. Nothing on how to make an object implement it. Is there no way to add listeners to javascript objects that aren't functions? Am I going to have to create an event handler object, or use 'document' or 'window' and have it call handlers? That seems really ugly.
Should the objects be functions in the first place? Would that work? It seems the current opinion is that making everything functions is just trying to make javascript into something it isn't.
AFAIK there are no way to add a event listener to the plain object, as it is not placed inside DOM. Events are firing inside DOM, and bubbling so your event listener for custom object won't receive it.
There is a http://www.bobjs.com/ framework that can help you implement custom events.
In response to #Barmar (sort of) I believe I worked this out. Confirmation on if this is a a good alternative or not would be nice, though. Basically, I have to do a subscriber thing, right? Almost event/listener, but not quite.
var thing = {
callbacks: {},
regCallback: function (key, which) {
callbacks[key] = which;
},
remCallback: function (key) {
callbacks[key].delete;
}
update: function(e) {
for(var i = 0, len = callbacks.length; i < len;i++){
callbacks[i](e);
};
}
};
var Room = {
updateHandler: function () {
//handle stuff
},
subscribe: function (which, callback) {
which.regCallback('room', callback);
}
unsub: function (which) {
which.remCallback('room');
}
};
//wherever/whenever I need to get updates something like
Room.subscribe(thing, Room.updateHandler);
//unsub
Room.unsub(thing);
Second error is caused by typo: addEventListerner has extra r in it.

setTimeout vs. Event binding/unbinding; what's more efficient?

I have a situation where I need to use jQuery's $.fn.one() function for a click event, but I don't want it to apply to the next occurrence of the event (like it usually does), I want it to apply to the occurrence immediately after that, and then unbind itself (like .one() normally does).
The reason I don't want .one() to apply to the first occurrence is because I'm binding to the document from an event handler invoked earlier in the bubbling phase, so the first time it gets to document it'll be part of the same event. I want to know when the very next click event occurs.
Note: I do not want to use .stopPropagation() because it will potentially break other parts of my app.
Here are the two options I've come up with, though it seems like there must be a more elegant solution.
The double bind method:
$(document).one('click', function() {
$(document).one('click', callback);
});
The setTimeout method:
setTimeout(function() {
$(document).one('click', callback);
}, 1);
Both methods work just fine, but here's my question. I have no idea what the performance implications are for either setTimeout or frequent event binding and unbinding. If anyone knows, I'd love to hear it. But more importantly, I'd like some suggestions on how to measure this stuff myself for future situations like this.
I love sites like http://jsperf.com, but I don't know if it would really be helpful for measuring stuff like this.
And obviously, if someone sees a much better solution, I've love to hear it.
I find the double-bind method quite elegant - I think it accurately reflects your actual intent, and it only takes two lines of code.
But another approach is rather than using .one() you could use .on() and update the event object associated with the first event, adding a flag so that the callback will ignore the first time it is called:
function oneCallback(e) {
if (e.originalEvent.firstTimeIn)
return;
alert("This is the one after the current event");
$(document).off("click", oneCallback);
}
$("div.source").click(function(e) {
e.originalEvent.firstTimeIn = true;
$(document).on("click", oneCallback);
});
Demo: http://jsfiddle.net/q5LG4/
EDIT: To address your concerns about not modifying the event object (or any object you don't own) you could store a firstTime flag in a closure. Here's a rather dodgy .oneAfterThis() plugin that takes that approach:
jQuery.fn.oneAfterThis = function(eventName, callback) {
this.each(function() {
var first = true;
function cb() {
if(first){
first = false;
return;
}
callback.apply(this,[].slice.call(arguments));
$(this).off(eventName,cb);
}
$(this).on(eventName, cb);
});
};
$(someseletor).oneAfterThis("click", function() { ... });
I'm sure that could've done that more elegantly (perhaps I should've bothered to look at how jQuery implements .one()), but I just wanted to whip something up quickly as a proof of concept.
Demo: http://jsfiddle.net/q5LG4/1/

Safely using hook events in JavaScript

In source code here
http://www.daftlogic.com/sandbox-javascript-slider-control.htm
There is these instructions:
// safely hook document/window events
if (document.onmousemove != f_sliderMouseMove) {
window.f_savedMouseMove = document.onmousemove;
document.onmousemove = f_sliderMouseMove;
}
I don't understand what it does and why it would be safer to do that this way, does someone understand?
It might be that some other code already assigned an event handler to document.onmousemove. The problem with this method, as opposed to addEventListener, is that only one function can be assigned to element.onXXXX. Thus, if you blindly assign a new event handler, an already existing one might be overwritten and other code might break.
In such a case, I would write:
if (document.onmousemove) {
(function() {
var old_handler = document.onmousemove;
document.onmousemove = function() {
old_handler.apply(this, arguments);
f_sliderMouseMove.apply(this, arguments);
};
}());
}
else {
document.onmousemove = f_sliderMouseMove;
}
This way it is ensured that both event handlers are executed. But I guess that depends on the context of the code. Maybe f_sliderMouseMove calls window.f_savedMouseMove anyway.
It is just saving the current hook, presumably so it can call it at the end of its own hook method.
It avoids stamping on some other codes hook that was already set up.
You would expect the hook code to be something like:
f_sliderMouseMove = function(e) {
// Do my thing
// Do their thing
window.f_savedMouseMove();
}
[obligatory jquery plug] use jquery events and you can ignore problems like this...
It appears that this code is storing the function that is currently executed on a mouse move, before setting the new one. That way, it can presumably be restored later, or delegated to, if need be. This should increase compatibility with other code or frameworks.

Run function only if event default is/isn't prevented

Is there a way to run a function only if event.preventDefault() is called on an event (by another unknown function). This is for a jQuery plugin, so I don't have any knowledge of what other parts of the page might be doing. I've tried this:
Event.test = Event.preventDefault;
Event.preventDefault = function () {
alert('Success');
this.test();
}
but it doesn't work... just behaves as normal, with no errors.
Conversely, I want the opposite too... to call a function only if event.preventDefault() isn't called. In effect, to add a function to the default action for an event. Any ideas? Is all this at all possible?
Edit: Based on the comment, I've got a solution to the first problem: http://jsfiddle.net/nathan/VAePB/9/. It works in Chrome (alerts function preventDefault() { [native code] }, but IE alerts undefined. So IE won't let me define Event.prototype.test, but it will let me redefine Event.prototype.preventDefault. Weird. I'm sure I can come up with a solution to the the second problem based on this one if I can just get it to work in IE.
I'm not sure I've understand. Can't you just use event.isDefaultPrevented() like this
For the first problem, try something like this:
oldPreventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function() {
//do stuff
oldPreventDefault.call(this);
}
I don't know if that will work, but it might be worth a shot.
For the second problem, I would try something similar to live event handling. Put a listener on a parent element (i.e. body or a top-level div). If you can get your hook into preventDefault as noted before, you can use that to set a flag. If the event bubbles up to that element and your flag isn't set, do your extended behavior. Though this won't work with all events, since not all events bubble. Another way to tackle this problem might be to delay execution until the current stack has finished using setTimeout(0,...) and then checking the flag.

Categories

Resources