What is the technical difference between this:
$('#myid').keypress(function(e)
{
if(e.which == 13) {
alert('test');
}
});
and this:
$(document).on('keypress', '#myid', function(e) {
{
if(e.which == 13) {
alert('test');
}
});
I have a jQuery script which runs with the second example but not with the first
They're using two completely different events. The first one uses the keypress event:
$('#myid').keypress(function(e)
while the second one uses the click event:
$(document).on('click', '#myid', function(e) {
In addition to that, the first one is binding directly to the #myid element, while the second one is binding to the document and filtering events based on the #myid selector. The resulting observed effect of these two kinds of binding is probably the same, but they accomplish that effect in very different ways.
i have a Jquery script which runs with the second example but not with the first
That's very unlikely, since the .which property on a click event doesn't equal 13. Though it depends on what you mean by "runs". You may be observing something entirely different than what you're describing. For example, a key difference between these two types of binding is that the second one is used to capture events from dynamically-added DOM elements. So if you try to use the first example on DOM elements which are loaded via AJAX then it won't find those elements when attaching the event handler.
There are two things going on here, so I will split my answer accordingly.
1. With regards to event binding in your code:
$('#myid').keypress(function(e){//...});
Is binding the keypress event to the HTML element whose id is myid. Therefore any keypress event that occur within said element, such as focus, tab, etc; they will all be handled by the keypress function.
$(document).on('click', '#myid', function(e) { //... });
Binds the click event handler to the entire document (website), as well as the HTML element whose id is myid. This is the ability of the on function which I will further describe below.
The on event handler can be very useful if you plan on binding one or more events to one or more HTML elements. I like to use it particularly when doing Chaining, as it can be both syntactically and efficiently better than writing one event handler at a time.
2. With regards to event handlers:
The difference between on and keypress is that the keypress is a shortcut for the event handler:
on('keypress', function(e){//...});
Meaning it is the same thing when it comes down to what it does.
Whereas on is used to attach event handlers in general. Such as the click event handler:
on('click', function(e){//...});
As well as the keypress event handler:
on('keypress', function(e){//...});.
... and many others.
The documentation in jQuery establishes very clearly how these functions work, so if you have any more questions after reading through the docs linked here, feel free to ask :-)
Related
Is it a good idea to manage all click events under the document element? The DOM is being constantly manipulated, so instead of constantly registering new events for each newly created DOM element, can't I just assign one event handler on the document element? For example:
document.onclick = function(event) {
switch(event.target.id) {
case 'someid':
// SOME ACTION
break;
case 'someotherid':
// SOME OTHER ACTION
break;
default:
// A CLICK WITH NO ACTION
}
};
Yes. This pattern is called event delegation, you can find a great article on the blog of David Walsh
You should also take a look at the Element matches / matchesSelector API
-https://developer.mozilla.org/es/docs/Web/API/Element/matches
-https://davidwalsh.name/element-matches-selector
You can do this, but it's not as efficient as binding events to specific elements. It means your function will run if someone clicks in a place that isn't mentioned in any of your cases. And even if it is, it will have to search sequentially through your cases until it finds the right one.
A somewhat better way to do it is to use an object keyed off the IDs.
var handlers = {
"someid": function(event) { // some action
},
"someotherid": function(event) { // some other action
},
...
}
document.onclick = function(event) {
if (handlers[event.target.id]) {
handlers[event.target.id](event);
} else {
// default action
}
}
This addresses the sequential searching problem, but it still runs when someone clicks on an unbound element. This probably isn't much of an issue for clicks, but imagine doing the same thing for mouse movement events, which occur almost constantly.
Also, this doesn't generalize easily to binding handlers to classes or more complicated selectors.
What you're doing is similar to how jQuery implements .on() event binding, when you write:
$(document).on("click", "someSelector", handlerFunction);
This form is generally only used when specifically needed, which is when the elements that match the selector are created dynamically -- it allows you to define the handler once, not add and remove it as elements change. But for static elements, we generally use the simpler
$("selector").on("click", handlerFunction);
because then the browser takes care of running the handler only when one of the selected elements is clicked.
So, there are two important details to this question:
its inside the scope of document ready's callback function
the element that the event is attached to does not actually exist in the DOM
Here's a visual representation of the scenario
$(document).ready(function() {
$('#myNonExistentElement').on('click', function() {
//do something
});
});
Is it possible to programatically trigger that click event (via console or something else) under those circumstances?
I think the simple answer is no.
There are two cases which might, however, fit with your question:
1) If you just want to execute the event handler code, use a named function (instead of an anonymous function) and call it whenever you need to.
2) If you want to bind a click handler to an object that does not yet exist in the DOM but you know will in the future, you can use code like:
$(document).ready(function() {
$('body').on('click', '#myNonExistentElement', function() {
//do something
});
});
See the section about delegated events at http://api.jquery.com/on/
If you try to bind an event to an element that doesn't exist via jQuery (or at the very least, .on) no new event will be bound.
Sample case here.
*event code stolen from here because I'm lazy.
Jquery bind is amazing, but I don't know in what order the binding happens. My current problem is thus:
$(document.body).delegate('form', methods.checkForm);
$('form').bind('submit', methods.submitFormIfCheckedFormIsTrue);
methods.checkForm = function (e) {
if (!$(this).isVerified()) {
return false;
}
return true;
};
methods.submitFormIfCheckedFormIsTrue = function (e) {
e.preventDefault();
$.ajax("/submitForm", {
data:$(this).serialize(),
type:"post"
});
};
This is obviously not the actual code that I'm using, but it's pretty close. What happens is, the submitFormIfCheckedFormIsTrue function fires before, or during, the checkForm function, so the checking is pretty useless. My hack for it was to add the class "checked" to the form and the check if the form has that class in the other function...but then you click submit, it checks, then you have to click it again to submit it if everything went right...which is retarded.
Another thing that's important regarding this problem is that I'm they're in completely different parts of my application, for reasons that can't change. Also, they're being loaded asynchronously.
The main thing I want to know then...is how to change the order, or set the priority of the events somehow...
If you are using 'delegate' the way you have it in your example, then the ajax submission is always going to run first, so the short answer to your question is "You Can't". Your delegate is attached to the 'body' element, so events attached to elements closer to the form in the DOM tree will fire first.
Events bubble from the form -> body, so there is no ordering when you are doing that.
One option would be to have your verification trigger a second event.
methods.checkForm = function (e) {
e.preventDefault()
if ($(this).isVerified()) {
$(this).trigger('form-verified');
}
};
Then instead of binding the other handler to 'submit', you would bind it to 'form-verified'.
$('form').bind('form-verified', methods.submitFormIfCheckedFormIsTrue);
This is also another way to accomplish ordering event if they are attached to the same element instead of using delegate.
Also, if you are using jQuery >= 1.7, then you should be using on instead of bind and delegate. http://api.jquery.com/on/
Update
If both are bound to the same element, then they will be triggered in the order that they were attached to the element. Assuming checkForm is bound before the other one, then the issue is that return false; does not stop other events from firing if they are attached to the same element. For that you also need e.stopImmediatePropagation().
methods.checkForm = function (e) {
e.preventDefault()
if (!$(this).isVerified()) {
e.stopImmediatePropagation();
}
};
There is also a useful answer over here if you ever have to tweak the ordering of events. jQuery event handlers always execute in order they were bound - any way around this?
In a general sense event handlers will be called in the order that they were bound, but only if they're bound at the same level. In your case you're binding one directly to the form while the other is a delegated handler bound at the document.body level. The directly bound one will happen first and then the event bubbles up to be handled by the other.
If you bind both handlers at the same level with .delegate() then they should be called in order:
$(document.body).delegate('form', 'submit', methods.checkForm);
$(document.body).delegate('form', 'submit',
methods.submitFormIfCheckedFormIsTrue);
Then in the first (generic) handler you should call the event.stopImmediatePropagation() method to prevent other handlers being called (noting that simply returning false prevents the default and stops the event bubbling up further, but it doesn't stop other handlers at that level from running):
methods.checkForm = function (e) {
if (!$(this).isVerified()) {
e.stopImmediatePropagation();
return false;
}
return true;
};
(By the way, the code shown in the question left out the event (second param) from the .delegate() call - I've put it in in my code.)
Or bind both handlers directly rather than using .delegate(). And speaking of using .delegate(), if you're using the latest version of jQuery you may like to switch over to using .on(), the new do-everything event binding method.
"What happens is, the submitFormIfCheckedFormIsTrue function fires before, or during, the checkForm function"
Definitely before, not during. (In pretty much all browsers) JavaScript runs on a single thread, so you will not ever have two functions running simultaneously.
Assuming that there are a large number of elements throughout the site that have an unknown number and type of events bound to them.
If I need to override all of these events with one single bound event, and only that event will fire, what are some recommendations?
I would be binding the event to a click event handler, and I am using jQuery.
Thanks in advance.
You’re looking for jQuery#unbind.
To remove all event handlers on an element or a set of elements, just do:
$('.some-selector').unbind();
To unbind only click handlers, use unbind('click'):
$('.some-selector').unbind('click');
To unbind all click handlers and immediately bind your own handler after that, you can do something like this:
$('.some-selector').unbind('click').click(function(event) {
// Your code goes here
});
Note that this will only work for events bound using jQuery (using .bind or any jQuery method that uses .bind internally). If you want to remove all possible onclick events from a given set of elements, you could use:
$('.some-selector')
.unbind('click') // takes care of jQuery-bound click events
.attr('onclick', '') // clears `onclick` attributes in the HTML
.each(function() { // reset `onclick` event handlers
this.onclick = null;
});
I would like to provide a thought without removing all events all together (just override them).
If your new one single bound event (we call it "click" here) is specific to the element it binds to, then I believe you can ignore any other events simply by stopPropagation() function. Like this
$("specific-selector").on("click", ".specific-class", function (e) {
e.stopPropagation()
// e.stopImmediatePropagation()
/* your code continues ... */
});
It will stop events bubbles up, so your other events won't fire. use stopImmediatePropagation() to prevent other events attached onto the same elements as "click" does.
For example, if "mouseleave" event is also bind to $("specific-selector .specific-class") element, it won't fire, too.
At last, all other events won't fire on this element but your new "click" element.
The unsolved question is, what if other events also use stopPropagation()? ... Then I think the one with best specification wins, so try to avoid complex, too many events is final suggestion.
You can see "Direct and delegated events" on jQuery site for more information.
Looks like this is pretty simple actually:
$('#foo').unbind('click');
$('#foo').bind('click', myNewFunction);
Thanks for your responses though.
Try to use live instead of bind. Then you can easily remove live binding with die from selector which is fast operation and set another live equally fast.
$('selection here').live('..', .....); // multiple invocations
$('selection here').die();
$('selection here').live('click',.....);
DOM is not touched at all. Event condition is evaluated on event occurrence.
But generally if you just want to swap handler functions why not to do it this way:
var ahandler = function(evt) { /* first implementation */ }
$('.selector').bind('click', function(evt) { ahandler(evt); });
//and then if you want to change handlers
ahandler = function(evt) { /* new implementation */ };
This gives absolutely no cost of any changes, rebinding etc.
I have HTML similar to the following in my page
<div id="someDiv">
<img src="foo.gif" class="someImg" />
</div>
The wrapper div is set up such that when it is clicked, it's background-color changes using the following jQuery code.
$("div").click(function(event){
$(this).css("background-color", "blue");
});
I also have some jQuery associated with my img that will do some other function (for the sake of argument I am going to display and alert box) like so:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
});
The issue I have come across is that when I click on the img, the event associated with the div is also triggered. I'm pretty sure that this is due to the way that jQuery (or indeed JavaScript) is handling the two DOM elements - clicking the img would require you to also technically click the div, thus triggering both events.
Two questions then really:
Is my understanding of the
DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
Are there any jQuery methods that
would allow me to perform actions on
a child element without invoking
those associated with its parent?
That is known as event bubbling, you can prevent it with stopPropagation():
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
.
Is my understanding of the DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
That is because of what is known event bubbling.
Are there any jQuery methods that would allow me to perform actions
on a child element without invoking
those associated with its parent?
Yes, you need stopPropagation()
No, this is by design. Events bubble up through the entire dom, if you put another handler on body, it would fire too
Yes :) JQuery normalizes the event object, so adding event.stopPropagation() in your img click handler will give you the behavior you expect on all browsers
The problem you just facing is called "event bubbling". That means, if you click on a nested
element, that click event will "bubble up" the DOM tree.
If other elements also are bound to an click event, their listeners will fire aswell.
Solution to prevent this is called:
stopPropagation()
which is used within your event handler
$("img[class=someImg]").click(function(event){
event.stopPropagation();
alert("Image clicked");
});
This is what's called event bubbling, and you can stop it to get the behavior you want with .stopPropagation() (or return false; if you want to stop the event completely, including handlers on the same level), like this:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
You can view a demo here, comment it out and click run again to see the difference.
The short version is that when most event types happen, they happen on the immediate element, then bubble up the DOM, occurring on each parent as they go. This is not jQuery specific at all, native JavaScript does this. If you're more curious, I'd read the linked article, it has a great explanation of what's going on.