A better way to search for events? - javascript

I recently found myself in the situation that I needed to remove a function bound to the resize event of the window by WordPress' media manager (media-upload.js), because it was interfering with the proper use of Thickbox. The event is attached like this:
a(window).resize(function(){tb_position()})
It took me a while, but I finally found out I could do it in this way:
jQuery.each( jQuery(window).data('events')['resize'], function(i, event) {
var thisEvent = event.toString().replace(/\n/g, '').replace(/\t/g, '').split(' ').join('');
var expectedEvent = 'function(){tb_position()}';
if (thisEvent == expectedEvent)
delete jQuery(window).data(‘events’)[‘resize’][i];
})
Here I cycle through the events, removing spaces, tabs and new lines from them and compare them to what I'm looking for, and when I find it I throw it out of the goddamn airlock. It happens in this case that the attached function perhaps doesn't have spaces, tabs or new lines, but this way also works with more complicated functions as far as I can tell.
Is there an easier and/or more elegant way of doing this? Is this a recipe for disaster down the road?

When you register a handler for an event, you can use a qualifier:
$('#something').bind('click.removeMeSomeday', function() { ... });
Then when you need to remove it you can do so without bothering other handlers for "click".
Now, it occurs to me that you may not be able to affect the way that Wordpress binds its event handler.

Another way around might be to use WordPress' system for queueing/unqueueing or registering/deregistering scripts. Unregister media-upload.js, and then queue your own version of it.
http://codex.wordpress.org/Function_Reference/wp_enqueue_script
http://phpxref.ftwr.co.uk/wordpress/nav.html?wp-includes/functions.wp-scripts.php.source.html#l74
http://phpxref.ftwr.co.uk/wordpress/nav.html?wp-includes/functions.wp-scripts.php.source.html#l37
Prem

Related

Action fired on multiple events

In CKEditor 4 I want to fire some action on key and paste events. I've got working code for single event:
$('#some_id').ckeditor({
some: config
}).ckeditor().editor.on('key', function(evt) {
//some action here
});
And I don't want to repeat all code for next event. I've searched ckeditor docs - and it says that on method takes only string, so give it an array of events isn't possible. I've tried pass multiple events as string key, paste - it wasn't best idea. Another way could be made an array of events and iterate it with code above - this solution seems to be not ideal, but the best I can figure out for now. Have You any better ideas for this problem?
Since nobody have any idea in this matter, I finished with best solution I could figure out on this moment: provide array of events and iterate it. I paste here my solution for others facing same dillema:
var editor = $('#textarea').ckeditor({
//some:config
}).ckeditor().editor;
var events = ['event1', 'event2'];
for (event of events) {
editor.on(event, function(evt) {
//Yours actions
}
}

Appending function to Custom Event in Prototype JS

I'm working with a 3rd party product where I am extending the UI with my own custom functionality. Within part of that I need to call an event after the UI has been updated with an AJAX call. Luckily the app fires a call to a Custom Event using the Prototype JS library after the call is complete, like this:
$(document.body).fire("ns:customevent");
If I add my own custom event with the same name then this works as expected
$(document).observe("ns:customevent", function(event) {
//do custom stuff here
});
[I could not get $(document.body).observe() to work here but I don't think that really matters.]
My concern here is that there may be other parts of the app that have registered functions against that event, and I am (blindly) overwriting them with my own, which will lead to issues further down the line.
Does Prototype append custom functions even though they have the same name or does it in fact overwrite them? If it does overwrite them then how can I append my function to anything that is already existing? Is there anyway of viewing what
I would imagine something like this, but I hardly know Protoype and my JS is very rusty these days.
var ExistingCustomEvent = $(document.body).Events["ns:customevent"];
$(document).observe("ns:customevent", function(event) {
ExistingCustomEvent();
//do custom stuff here
});
I can't add my event handler or add in code to call my own function, I want to try avoiding the 3rd party library (if that would even be possible).
Thanks.
As an FYI for anyone else that stumbles upon this question, following the comment from Pointy it turns out that Prototype does append the functions to the custom event.
I verified this by trying the following and both alerts fired.
$(document).observe("ns:customevent", function(event) {
alert("ALERT 1");
});
$(document).observe("ns:customevent", function(event) {
alert("ALERT 2");
});
Great :)

Spine.js - How do you unbind a specific event handler from a model instance using JavaScript?

It's pretty easy to unbind specific event handlers from model classes, but it seems the only way to unbind an event handler from an instance of a model is to unbind ALL event handlers attached to that instance using unbind() (takes no arguments).
Is there an equivalent to Model class' unbind([eventName, function]) function for model instances, or is there another better way to unbind specific handlers without unbinding them all?
I've looked through the Spine.js documentation but no luck so far. Adding arguments to the unbind() function has no effect - it just ignores them and unbinds all anyway.
In the end we switched to a more suitable framework for our needs, Backbone.js. Maintenance on the Spine.js GitHub repository seems to have ground to a halt in September last year and, though Spine supports use with JavaScript, the documentation for it is pretty poor.
If anyone can provide a working solution to the original problem I'll still accept it for the benefit of anyone else who stumbles across this.
I don't think this specifically answers your question but might get you to what you are going for.
Spine has a not so documented .one() event
from the source:
one: (ev, callback) ->
#bind ev, ->
#unbind(ev, arguments.callee)
callback.apply(this, arguments)
So it basically does the unbind for you behind the scenes, but it doesn't use a Model unbind which actually just triggers 'unbind'.
unbind: (ev, callback) ->
unless ev
#_callbacks = {}
return this
list = #_callbacks?[ev]
return this unless list
unless callback
delete #_callbacks[ev]
return this
for cb, i in list when cb is callback
list = list.slice()
list.splice(i, 1)
#_callbacks[ev] = list
break
this
vs.
unbind: ->
#trigger('unbind')
We use #item.one in a few places and have found it works okay.
example use:
#item.one 'awaitingPermit', (item) =>
#navigate('/document', item.id, 'show')
update:
We have worked on some solutions to this missing feature in Spine.js. see this issue thread https://github.com/spine/spine/issues/418

Keeping bookmarklet from generating multiple event listeners

I currently am working on a bookmarklet that opens an iframe, and sets up a communication of postMessage back and forth. That all works fine.
However, seemingly because the bookmarklet is being loaded as an anonymous function, the listeners are multiplying if I run the bookmarklet more than once on a page.
Is there some sort of way to keep track of these addEventListeners so that they don't double-up?
Do I need to define the rp_receive_message outside of the anonymous function?
Here's an example of the code:
var rp_receive_message = function (e) {
var response = e.data;
console.log("got message with "+ response);
};
if (window.addEventListener) {
window.addEventListener('message', rp_receive_message, false);
} else {
window.attachEvent('onmessage', rp_receive_message);
}
var s1 = window.document.createElement('iframe');
s1.setAttribute('src', 'http://mydomain.com/iframe.html');
s1.setAttribute('id', 'testiframe');
s1.setAttribute('width', '700');
s1.setAttribute('height', '550');
s1.setAttribute('frameBorder', '0');
s1.setAttribute('onload', 'this.contentWindow.postMessage(window.location.href, "http://mydomain.com/iframe.html");');
document.getElementById('container').appendChild(s1);
Probably this will solve the problem:
window.onmessage = rp_receive_message;
As you suggest, the code below might be enough by itself. I don't know if addEventListener and attachEvent will add the same function multiple times, but I wouldn't at all be surprised if they will. I suggest just testing it.
window.rp_receive_message = function(){...}
If you dislike either solution, you've got to set up a global variable, which hardly seems any different or greatly superior to above. The global can be a simple boolean to check if the event has been attached, or it can be a list of attached events that you update yourself. AFAIK, and I'm pretty sure, there is no native JS solution to get a list of event listeners have been attached to a particular event. Libraries such as jQuery maintain lists and let you read them; and possibly have other techniques that are elegant solutions to your general problem.

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/

Categories

Resources