I want to attach a "mouseup" event on all nodes of a container, including text nodes using jQuery. How do I do that?
Update:
If I had some HTML fragment like this:
<p>Some text node <strong>strong text</strong> another text node.</p>
Currently, $("p *") will apply the event to the <p> and <strong> but not to the 2 textnodes within <p> separately. Modifying the source to add classes is not an option.
bobince is right that you cannot set event handlers on a Text node. It sounds like you want something like
<p><span>Some text node</span> <strong>strong text</strong><span> another text node.</span></p>
and the events would get attached to the span tags. However that wouldn't work if you can't change the source.
While Tatu's answer ought to work (except use .mouseup instead of .click), are you sure you actually need an event handler on every single node? If you bind using
$('#container').mouseup(function(event){//code})
the event bubbling model will call that same func anytime the mouseup event occurs on any element inside #container, and the DOM node that actually triggered the event will be contained in the event.target property. This is much more efficient in most cases.
You cannot set event handlers on a Text node. Text nodes do not implement the EventTarget interface like Element nodes, the Document node and the window object do.
You should never need to, either. Set one mouseup handler on the parent element and you will get mouseup events for all its child content. This is because the mouseup event ‘bubbles’ up through its ancestors.
$('#container *').mouseup(function() { ... });
That will bind the mouseup event handler to all nodes inside #container.
EDIT
Changed click -> mouseup to be clearer.
I think the easiest would be to add a class to all your nodes.
The jquery selector will be as simple as :
$('.yourClassName').dostuff()
Related
It seems that the mouseout callback from jQuery attached to the div tag containing several children elements will be called each time when user dragged a mouse away from any of these children elements. Am I right?
If so, how can I intercept the top-level tag mouse event only?
jQuery's mouseout bubbles, which means you can put an if statement to check and see if the target is the parent and put your code inside that.
https://jsfiddle.net/ck0kbowt/ for example :)
My code is this:
document.addEventListener('click', function(ev){
if(ev.path[0].className == 'linkTogether'){//do something}
if(ev.path[0].id == "createNewPage"){//do something}
});
Which has actually been working well for dynamically created buttons and nodes, but something just feels off about it. So I'm wondering if this is best practice or if there is a better way to add event listeners to dynamically created elements.
Thanks,
Jack
In specific cases where you have huge amount of objects that behave in the same way you can use this technique (adding event listener to their parent) to improve the performance of your script.
In a general page however you just have to many different objects and iterating trough all of them to check which one you've clicked is not faster.
Here is an article for the technique you are referring to - event delegation.
This is the proper pattern for creating event listeners that will work for dynamically-added elements. It's essentially the same approach as used by jQuery's event delegation methods (e.g. .on).
However, it does have performance implications. Every time you click anywhere in the document, the code will run, and have to go through the entire list of event bindings that you need to check. You can improve this by adding your event listener to a more specific element. If the dynamic elements are always added inside a specific DIV, add your listener to that DIV rather than document.
This also avoids another pitfall of event delegation. Event delegation depends on the event bubbling up from an inner element to all its containers. But if there are any handlers along the way that call event.stopPropagation, the event won't make it out to document. If you add the listener to a lower element, you're less likely to have a conflict like that.
And I would like to add, if you create elements dynamically, better you include the listeners inline and filter in the function what you want to do.
<div class="form_ID1" onclick="myfunction(this, event);">
... children bubbling
... Use if statements in caught js myFunction function.
In myfunction function you will capture the child element by event.target and the element that contains the listener by this.className.
There are scenarios however that you need a universal ( document ) click event. e.g You need to close a pop up when you click outside of a pop up !! It is like: e.g.
if (clicked.className != popup.className) popup.remove()
Even in this case there is a workaround by inserting an onblur="myfunction(this); in the parent DIV of the popup.
I have a set of inputs where I would like to add another input when the last input currently displayed receives focus. The form starts out with 2 inputs.
$('.answer_fields input:last-child').focus(function(event) {
var nextfield = $('.answer_fields input').length + 1;
$('.answer_fields').append('<input class="form-control answer_field" placeholder="Answer option #' + nextfield +'" id="answer' + nextfield +'">');
});
As it currently stands, additional fields are only appended when the 2nd input (the original "last-child") receives focus.
Checking the source seems to show that the inputs are getting added to the DOM as expected. What am I missing?
The jQuery selector is evaluated ONCE at the time you run the code to install the .focus() event handler. It isn't adjusted live as things change. You can switch to event delegation if you want that type of behavior.
A similar event handler using event delegation that will be evaluated live would look like this:
$(some common parent selector).on("focus", ".answer_fields input:last-child", function() {
// code here
});
Working demo: http://jsfiddle.net/jfriend00/ZZLPJ/
First off, your original code did not work because you were attaching event handlers to specific elements that existed at the time you ran your first code. Which events you had event handlers attached to did not change as your structure changed in the future so new elements would not have event handlers on them and the :last-child portion of your selector would not be re-evaluated.
Event delegation works because certain events that occur on a given element will bubble up the DOM hierarchy through each parent. You can attach a single event handler to a common parent and when that parent is notified that a given event occurred, it can then dynamically evaluate the selector you specified vs. the child item that originated the event and if it's a match, fire your event handler. This gives you the "real-time" selector evaluation that you want in your case.
To give you an idea, these are what I call "static" event handlers:
$(".test:last-child").focus(fn);
$(".test:last-child").on("focus", fn);
This a delegated (and thus dynamic) event handlers:
$("#parent").on("focus", ".test:last-child", fn);
For more info on this topic, see these other answers:
jQuery .live() vs .on() method for adding a click event after loading dynamic html
Does jQuery.on() work for elements that are added after the event handler is created?
Should all jquery events be bound to $(document)?
Just checked to be sure and the focus event does bubble event delegation will work with it.
This is a common question for people starting to use jQuery. It turns out that the named eventListeners (i.e. $.fn.click, $.fn.focus, etc.) attach an event listener to each matching DOM element when the page is first loaded. These listeners are defined on the matching element themselves, meaning that each one is unique to the element it is attached to.
You will hear this method compared to what is commonly known as event delegation. Event delegation involves attaching an even listener to a shared parent element that will check the event.target attribute to see if it matches your criterion (in your case, .answer_fields input:last-child). This differs from attaching a unique listener to each DOM element you wish to target because it allows you to add new nodes to the DOM dynamically, which will then be handled by the parent node's listener like any other node.
To accomplish event delegation using jQuery, use the .on() function on a common ancestor (I'll use document as an example) and listen for all matching elements as the second argument of the function call:
$(document).on('focus', '.answer_fields input:last-child', function(event) {
// your code here
});
Not only will this solve your problem with dynamically created DOM elements, but event delegation will greatly improve the performance of your page by reducing the total number of event listeners attached to elements in the DOM.
For more information about event delegation, I'd encourage you to check out this tutorial and this SO question
Generally, when I want to bind some event to an element, I will bind the event to the element directly. For example, I want to bind the click event to the "li" element:
<ul id="ul_list">
<li class="t">xxxx</li>
<li class="t">xxxx</li>
.....
</ul>
var lis=document.getElementById("ul_list").children();
for(var i=0;i<lis.length;i++){
lis[i].onclick=function(){
console.info(this.innerHTML);
}
}
It works.
But in some open source code, I find that people prefer to bind the event to the parent element:
document.onclick=function(e){
e=e==null?:window.event:e;
var target=e.target; //the browser is not considered here
if(target.className=='t' && target.localName='LI'){
console.info(target.innerHTML);
}
}
I wonder which is better?
Also, when handling drag events, people bind the mousemove event to the whole document. Why?
people prefer to bind the event to the parent element
This is referred to as event delegation and is especially useful when you want to trigger the same event handler for multiple elements. Instead of binding an event handler to each those elements, you bind it to a common ancestor and then check from which element the event originated. This works, because events bubble up the DOM tree.
I wonder which is better?
It depends, both approaches have their pros and cons.
Event delegation can be slower, as the event has to bubble up first. You also might have to perform DOM traversal because the event might not originate at the element you test for. For instance, in your example, the li elements might have other children, lets say an a element. To find out whether the clicked a element is a descendant of one of the lis, you have to traverse the ancestors and test them.
On the other hand, binding the handler directly is faster in the sense that the event is processed directly at the element. But if you bind a lot of event handlers and don't do it properly (like in your example) you use more memory than you actually need. Older browsers (I think especially IE) might also perform worse if there are many event handlers.
Also,sometime when handle the drag effect,people always bind the mousemove event to the whole document,why?
The problem is that while dragging an element, the mouse often moves faster than the element and leaves it. If you bind the mousemove event only to the dragged element, whenever the cursor leaves the element, the movement would stop. To avoid this, the event handler is bound to the whole document (for the duration of the dragging) so that the movement is smoothly.
Linking to the parent means you are adding one event handler instead of multiple. That is a boost for performance in your application! Also if you add new elements to the page, you do not have to worry about binding them.
Only time when that model is a bad design is when you need to cancel the event. For example, you have links in the li and you need to prevent them from doing their default action.
In case of click event, it's better to bind directly to the affected elements, no point binding to everything - why do you need to have your function trigger when you click something that is totally not relevant to you?
It can be useful when the elements are spread over the document and hard to "collect" them, e.g. when they only have the same class - getElementsByClassName is not very efficient in pure JavaScript as you need to iterate over all the elements, so in such case it's better to trigger the function always and check what has been clicked.
Bind handlers to the most specific event and element possible.
Note that (just to be pedantic!) you bind a handler to an event for an element, not 'bind some event to an element'.
My question is:
Does have a span element the inner html change event?
I think about I have a span and when the span inner html is changing it will throw an event that I can reach?
I would like to use Jquery to bind to this event of span.
l.
From the jQuery documentation:
The change event is sent to an element
when its value changes. This event is
limited to <input> elements,
<textarea> boxes and <select>
elements.
At the moment, such events are not supported by browsers like IE, but what you are looking for is DOM events. See https://developer.mozilla.org/en/DOM_Events for more information.
No. As a rule of thumb, if something internal to a script changes something, it will not trigger an event.
Nothing external to a script can edit the innerHTML of a span (unless, perhaps, it is contentEditable) so there is no event.
Why not handle it when you make the change?
function updateHTML(el,newhtml,callback){
el.innerHTML=newhtml;
if(typeof callback=='function')callback(el);
}