I am currently in the process of integrating the dropkick.js plugin into my app, but I have run into a few snags. When I change backbone views the events do not work properly and the .live() event associated in dropkick.js just flat out doesn't work at all. Nothing fires. I decided to upgrade this to using the .on() function and got it sort of working (even though it still deletes my url for some reason).
This doesn't work at all:
$(document).on("click", ".dk_toggle", function() {
This only works somewhat:
$(".content").on("click", ".dk_toggle", function() {
Do you know why document doesn't work at all?
My backbone $el is $(".content").
Instead of document, use body. It basically gives the same behavior.
$('body').on("click", ".dk_toggle", function() {
//....
});
The first example demonstrates event delegation.
The second example binds the event handler directly to the element. The event will still bubble (unless you prevent that in the handler) but since the handler is bound to the target, you won't see the effects of this process.
Test Run
Related
I need to change behavior of jQuery library (date range picker), it have code like this:
box.find('.month').off("change").change(function(evt) {
dateChanged($(this));
});
box.find('.year').off("change").change(function(evt) {
dateChanged($(this));
});
Those are two select elements. It don't return false and functions inside handler don't access the event. But for some reason my events that use delegation doesn't work. They are ignored.
$picker.on('change', 'select', function() {
console.log('CHANGE');
});
The console log is not executing, but if I remove previous lines from the library, my event delegation code works fine.
NOTE: $picker is object in my code that is parent of box element. But I also have event added on $(document) that is also not working.
First time I see something like this. Adding event directly to element, prevents event propagation. Can someone explain what is happening here? Is this documented anywhere?
This happens in Firefox and Chrome.
If someone need simple example, I can create one. But thought that this is self explanatory.
EDIT: I've created a simple reproduction and it works fine. I have complex application with a lot of files (R Shiny Application), but I don't see any change events in dev tools. Are there any way of making the event not propagate? Maybe using event capturing. What should I search for in order to find the code that is preventing the events from propagating?
What are the advantages of using jQuery's
$(window).blur(function() { ... })
to attach an event handler versus setting it directly with
window.onblur = function() { ... }
It seems that the latter is less robust because it only supports one blur handler, and when used with other packages, other code might override the window.blur value with another function. However, couldn't this also happen with the jQuery implementation too, which presumably uses window.blur as its underlying implementation?
EDIT: Several people have also mentioned the window.addEventListener alternative, which can be used to add an 'onblur' event apart from the methods above.
$(window).blur(function() { ... })
Lets you add one or more event handlers.
window.onblur = function() { ... }
Lets you only have one event handler handling the blur event.
The former uses the jQuery's own event handle mechanism. The call to .blur() will delegate to jQuery.fn.on() which in turn will delegate to jQuery.event.add. This add() method will create it's own handler for the given event type and tell addEventListener() to call this handler whenever a event of given type is fired. So basically jQuery has it's own way of event handling which relies on addEventListener() to execute properly.
The latter is just an attribute which can only contain one value so queueing event handlers is impossible.
I wrote a little demonstration to prove this point: http://jsfiddle.net/GnNZm/1/
With the jQuery method, you can attach multiple event handlers. By setting window.onblur, you can only have a single handler.
Pure JavaScript also has this: window.addEventListener(). In fact, i'm sure jQuery uses this internally. (Yes they do.)
(EDIT)
The window.onblur property is basically a shortcut for setting a single handler. Using addEventListener() (or the jQuery wrapper) basically creates a list of event handlers, which all get fired when the event happens. I haven't tested, but i think you can even use the two together. Because it's a list, not a single value, multiple handlers shouldn't interfere with each other. They can also be removed separately or all at once.
jQuery's event handlers, using on(), also let you namespace your handlers, to prevent clashes if a plugin removes its handlers. Pure JS doesn't seem to have this easily.
For jquery blur
The blur event does not bubble in Internet Explorer. Therefore,
scripts that rely on event delegation with the blur event will not
work consistently across browsers. As of version 1.4.2, however,
jQuery works around this limitation by mapping blur to the focusout
event in its event delegation methods, .live() and .delegate().
taken from jquery doc https://api.jquery.com/blur/
Also jquery allows you bind multiple event handlers
When you attach an event there is the possibility of overwriting an event already attached to an event handler. This used to happen a lot with window.onload() where different scripts overwrote each others event handlers.
eg:
//lightbox.js
window.onload = function() { /* do lightbox stuff */ }
//carousel.js
window.onload = function() { /* do carousel stuff */ }
So the common practice used to be something like this:
var existing_event_handlers = window.onload;
window.onload = function(){
//my event code
alert('onready fired');
//call other event handlers after
existing_event_handlers();
}
Using window.onblur = function() { ... } still has an advantage because you can specifically dictate if you want your event fired before or after other attached events.
Like many other answers already pointed out jQuery abstracts you from most browser differences. Version before IE9 used attachEvent() rather than addEventListener().
There is a best practice to invoke a jQuery change handler immediately afters its definition to re-use the code and initialise the GUI, like
$("#mySelect").change(function() {
$("#myTextField").val( $(this).text());
}).change(); // here the element is immediately triggered, so one does not need a separate code path for the init-case
Without the immediate call using .change() the GUI would not reflect the initial value of #mySelect in #myTextfield.
With newer versions of jQuery I would like to use event-delegation and do the same stuff using the .on() API.
$("#myForm").on('change', '#mySelect', function() {
$("#myTextField").val( $(this).text());
}).change();
This does not work anymore, because the .change() is not triggered on the right element and not with the right event.target, so jQuery can't call my event handler.
This works, but does no longer reflect the best practice without the separate init code-path:
$("#myForm").on('change', '#mySelect', function() {
$("#myTextField").val( $(this).text());
});
$("#mySelect").change();
Question: Any good way to solve this, without re-selecting the element and triggering the event?
You would need to find all the elements and call trigger on them... Because delegation delegates the handling to some other element up the DOM (as you likely know)
You would essentially have to do what you have done in your 3rd example. I know of no other way to achieve this.
I am trying to bind click handlers to incoming ajaxed content. I used to use 'live'
$('#div').live('click', function(event) {
alert('I got clicked, Live style');
});
But now as my site is getting more complicated, I am realizing how crazy things can get using live and having everything bubble to the top of the DOM. Which is not ideal.
So I started using on(),
$('#div').on('click', function(event) {
alert('I got clicked, On style');
});
But I miss the fact that using live() I could just initialize the click handlers once and be done with it instead of reinitialize them every time new content is loaded. Is there a best of both worlds?
Is there a better way to "reload" click handlers to recognize new ajax content aside from creating the handlers in the ajax callback function? To me that seems highly suspect. Whats the appropriate way to do this?
As of jQuery 1.7 the following .on() event binding is equivalent to the deprecated live:
$(document).on('click', '#div', function(event) {
alert('I got clicked, On style');
});
You can also bind the event to some fixed element further down the DOM which doesn't get re-generated, this functionality would be the same as .delegate():
$('#parentofdiv').on('click', '#div', function(event) {
alert('I got clicked, On style');
});
It is advisable to use the second form to narrow down the scope of the event binding as much as possible to make it easier to maintain.
Edit: For the record, what you originally did in your post would be the preferred replacement for your .bind() calls in your code.
Have you looked at using .delegate? http://api.jquery.com/delegate/
jQuery's on() method can be used to attach various events to already existing items as well as items added by ajax calls to the DOM in the future:
$(document).on("click", ".ajax-added-content", function(event) {
alert('I got clicked, On style');
});
It is possible to do what you want with
.on()
and it is actually the recommended method.
.live()
is deprecated as of jquery 1.7.
You can attach your event to the body and use this overload of "on" to get the functionality you desire. Check the next to last example in jquery's doco of .on
$("body").on("click", "#div", function(){
alert('I got clicked, On style');
});
I'm currently upgrading my application to use jQuery 1.6.1 (previously using 1.4.4) and found that now the .click() event automatically triggers a .change() event as well.
I created a simple example here: http://jsfiddle.net/wDKPN/
Notice if you include 1.4.4 the .change() function will not fire when the .click() event is triggered. But when switching to 1.6, the .change() event is fired when .click() is triggered.
Two questions:
Is this a bug? It seems that programmatically triggering .click() shouldn't also fire other events (for example, it would seem wrong to also automatically fire .blur() and .focus(), to help "mimic" a user's click).
What is the proper way for me to bind a change() event and then trigger both a click() and change() event for that element? Do I simply call .click(), and rely on the fact that .change() will also fire?
$('#myelement').change(function() {
// do some stuff
});
$('#myelement').click(); // both click and change will fire, yay!
In my old code I'm using this pattern to initialize some checkboxes (and their checked states and values) after an ajax call:
$('#myelement').change(function() {
// do some stuff including ajax work
}).click().change();
But in 1.6.1 my logic fires twice (once for .click() and once for .change()). Can I rely on just removing the .change() trigger and hope that jQuery continues to behave this way in future versions?
Best way to do this is:
$('#myelement').bind('initCheckboxes change', function() {
// do some stuff including ajax work
}).trigger('initCheckboxes');
To do your initialization stuff you just bind to it a custom event, which you trigger it the first time the page loads. This, no one will take away from you on any versions.
Whereas change event, I believe, will continue to be there on all versions, because it has been for so long, and it just works nicely that way.
In the end, this is a happy ending story, because the custom event initCheckboxes will fire just once on page load and change event will always listen and fire on change state.
I would say this was a bug in jQuery 1.4.4. Removing the jQuery event handlers and using standard addEventListener produces the same result as jquery 1.6.1.
http://jsfiddle.net/jruddell/wDKPN/26/
window.count = 0;
document.getElementById('mycheckbox').addEventListener('change', function() {
window.count++;
jQuery('#output').append('I fired: ' + window.count + ' times<br />');
});
document.getElementById('mycheckbox').click();
Also I would use triggerHandler to specifically invoke a jQuery event handler. If you want the event model to determine which handlers to call, then use click, change etc.
Forget about the click event for checkboxes. The change event handles everything.
$('#myelement').change(function() {
// do some stuff
});
$('#myelement').trigger('change');
See for yourself: http://jsfiddle.net/zupa/UcwdT/
(This demo is set to jQuery 1.8 but works in 1.6 as well.)
I think you can make it work for both jQuery 1.4.4 and 1.6 implementations by putting change() within click handler and then triggering the click.
$('.myelement').click(function(){
$('.myelement').change();
alert($(this).val());
});
$('.myelement').trigger('click');
Have a look there for simple example.