I'm trying to avoid event capturing in a polymer custom element, so I want to trigger an event only when is provoke by the ['items'] contained in the element.
I have two different custom elements. Both custom elements are extending core-selector.
One is wrapping the other.
So I have something like:
<my-parent-element>
<parentItem>I'm a parent
<my-child-element>
<childItem>I'm a child</childItem>
<childItem>I'm a child</childItem>
<childItem>I'm a child</childItem>
</my-child-element>
</parentItem>
<parentItem>I'm a parent</parentItem>
<parentItem>I'm a parent</parentItem>
</my-parent-element>
Custom elements definition
<my-parent-element> has it's own core-Selected event handler.
<my-child-element> has no script neither event handlers.
My goal:
Only when a item from <my-parent-element> is selected the event is
triggered.
I want to maintain the components independence, so I would like not to modify the child.
Situation
Based on this answer I'm stopping the method checking the item received, but I think is not a proper solution...
onCoreSelect: function (event, detail, sender) {
if (!detail.item.classList.contains('child')) {
return; //Dislike!
}
);
...because I would like even not trigger the method.
You can reproduce it in this Plunker. onCoreSelect writes details, but only selecting parents should do it. As you can see, when clicking any children it triggers the event.
Thx!
Calling methods only when 'provoked by a item' is done using Declarative event mapping.
Here is the docs and example on it:
https://www.polymer-project.org/docs/polymer/polymer.html#declarative-event-mapping
Related
I am trying to understand this particular difference between the direct and delegated event handlers using the jQuery .on() method. Specifically, the last sentence in this paragraph:
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
What does it mean by "runs the handler for any elements"? I made a test page to experiment with the concept. But both following constructs lead to the same behavior:
$("div#target span.green").on("click", function() {
alert($(this).attr("class") + " is clicked");
});
or,
$("div#target").on("click", "span.green", function() {
alert($(this).attr("class") + " is clicked");
});
Maybe someone could refer to a different example to clarify this point? Thanks.
Case 1 (direct):
$("div#target span.green").on("click", function() {...});
== Hey! I want every span.green inside div#target to listen up: when you get clicked on, do X.
Case 2 (delegated):
$("div#target").on("click", "span.green", function() {...});
== Hey, div#target! When any of your child elements which are "span.green" get clicked, do X with them.
In other words...
In case 1, each of those spans has been individually given instructions. If new spans get created, they won't have heard the instruction and won't respond to clicks. Each span is directly responsible for its own events.
In case 2, only the container has been given the instruction; it is responsible for noticing clicks on behalf of its child elements. The work of catching events has been delegated. This also means that the instruction will be carried out for child elements that are created in future.
The first way, $("div#target span.green").on(), binds a click handler directly to the span(s) that match the selector at the moment that code is executed. This means if other spans are added later (or have their class changed to match) they have missed out and will not have a click handler. It also means if you later remove the "green" class from one of the spans its click handler will continue to run - jQuery doesn't keep track of how the handler was assigned and check to see if the selector still matches.
The second way, $("div#target").on(), binds a click handler to the div(s) that match (again, this is against those that match at that moment), but when a click occurs somewhere in the div the handler function will only be run if the click occurred not just in the div but in a child element matching the selector in the second parameter to .on(), "span.green". Done this way it doesn't matter when those child spans were created, clicking upon them will still run the handler.
So for a page that isn't dynamically adding or changing its contents you won't notice a difference between the two methods. If you are dynamically adding extra child elements the second syntax means you don't have to worry about assigning click handlers to them because you've already done it once on the parent.
The explanation of N3dst4 is perfect. Based on this, we can assume that all child elements are inside body, therefore we need use only this:
$('body').on('click', '.element', function(){
alert('It works!')
});
It works with direct or delegate event.
Tangential to the OP, but the concept that helped me unravel confusion with this feature is that the bound elements must be parents of the selected elements.
Bound refers to what is left of the .on.
Selected refers to the 2nd argument of .on().
Delegation does not work like .find(), selecting a subset of the bound elements. The selector only applies to strict child elements.
$("span.green").on("click", ...
is very different from
$("span").on("click", ".green", ...
In particular, to gain the advantages #N3dst4 hints at with "elements that are created in future" the bound element must be a permanent parent. Then the selected children can come and go.
EDIT
Checklist of why delegated .on doesn't work
Tricky reasons why $('.bound').on('event', '.selected', some_function) may not work:
Bound element is not permanent. It was created after calling .on()
Selected element is not a proper child of a bound element. It's the same element.
Selected element prevented bubbling of an event to the bound element by calling .stopPropagation().
(Omitting less tricky reasons, such as a misspelled selector.)
I wro te a post with a comparison of direct events and delegated. I compare pure js but it has the same meaning for jquery which only encapsulate it.
Conclusion is that delegated event handling is for dynamic DOM structure where binded elements can be created while user interact with page ( no need again bindings ), and direct event handling is for static DOM elements, when we know that structure will not change.
For more information and full comparison -
http://maciejsikora.com/standard-events-vs-event-delegation/
Using always delegated handlers, which I see is current very trendy is not right way, many programmers use it because "it should be used", but truth is that direct event handlers are better for some situation and the choice which method use should be supported by knowledge of differences.
Case 3 (delegated):
$("div#target").delegate("span.green", "click", function() {...});
I always wondered which is the better way of handling events in terms of code manageability, cleanliness and code reuse.
If you use the former then say a list of 10 anchor tags with click handler will have something like:
Click Me
Click Me
Click Me
... 10 times
which looks kind of odd.
With the latter method, using anonymous function, it'd be like:
$('a').on('click', function(e){});
At the end of the day, every event is bound to some element in the DOM. In the case of .bind, you're binding directly to the element (or elements) in your jQuery object. If, for example, your jQuery object contained 100 elements, you'd be binding 100 event listeners.
In the case of .live, .delegate, and .on, a single event listener is bound, generally on one of the topmost nodes in the DOM tree: document, document.documentElement (the element), or document.body.
Because DOM events bubble up through the tree, an event handler attached to the body element can actually receive click events originating from any element on the page. So, rather than binding 100 events you could bind just one.
For a small number of elements (fewer than five, say), binding the event handlers directly is likely to be faster (although performance is unlikely to be an issue). For a larger number of elements, always use .on.
The other advantage of .on is that if you add elements to the DOM you don't need to worry about binding event handlers to these new elements. Take, for example, an HTML list:
<ul id="todo">
<li>buy milk</li>
<li>arrange haircut</li>
<li>pay credit card bill</li>
</ul>
Next, some jQuery:
// Remove the todo item when clicked.
$('#todo').children().click(function () {
$(this).remove()
})
Now, what if we add a todo?
$('#todo').append('<li>answer all the questions on SO</li>')
Clicking this todo item will not remove it from the list, since it doesn't have any event handlers bound. If instead we'd used .on, the new item would work without any extra effort on our part. Here's how the .on version would look:
$('#todo').on('click', 'li', function (event) {
$(event.target).remove()
})
Second method is preferrable, since we should not be mixing our JavaScript with the HTML. (Separation of Concerns) . This way your code is kept clean.
This also works well with dynamically inserted HTML code.
`$('a').on('click', function(e){});` // Using jQuery.
Using Vanilla JS:
document.getElementById("idName").addEventListener("click", function(){}); // Bind events to specific element.
document.addEventListener("click", function(){}); // Bind events to the document. Take care to handle event bubbling all the way upto the document level.
I am trying to construct a wrapper for an element and I want to create a seamless integration for my wrapper. To achieve this, I want to propagate all events from one element to another, to create a mirror to say so.
Let's say I have #element-1 and #element-2. I give #element-1 a 'click' handler. The desired behaviour is that whenever a click occurs, either on #element-1 or #element-2, the handler should fire. Also, if #element-2 is assigned a different 'click' handler, both handlers should trigger.
Do not worry about infinite bubbling, I will find a mechanism to counter that. My question is how can I achieve this event mirroring for all possible events for these two elements, without writing all the event names (I'd also like to get custom events, so hard-coding the event names is not an option)?
I'm open to solutions that may not use jQuery, as long as it achieves what I described.
Suppose there are 10 anchor elements inside div ids #event-1, #event-2, ... #event-10. You can write the event handlers as below.
for(i=0;i<=10;i++) {
if ($(".event-"+i).find('a').length > 0) {
var g = $(".event-"+i).find('a');
g.click(function(){
/* Your code */
});
}
}
I need to have multiple .click() functions populated on page load, based on how many image records are stored within a mysql database.
so far i have a page that will nicely switch between photos with a <ul> of image buttons
but i have to hand write the jquery that deals with it.
is there a way that i can populate a .js file with the correct amount of .click() functions based on the amount of records on in the data base.
In addition to Alex's answer, if you want to set the click event of elements that don't exist yet or haven't been added to the page, you could do:
$(body).on('click','a.record',function(){
//any existing or future a element with class record will have this click function
});
Instead of adding a separate onclick handler to each element, you should use event delegation and attach a single event handler to some container. Said event handles would catch all the onclick events , as the bubble up through DOM.
You don't need to write a click() for each unique element.
Instead, you could select a bunch of elements with a selector, such as $('a.record') and then chain click() to that...
$('a.record').click(function() {
// Any `a` element with a class of `record` was clicked.
});
The disadvantage of doing it this way is you add a bunch of event listeners and it won't be triggered for future elements.
As others have mentioned, event delegation using on() (if using a newer jQuery) or delegate() (if using an older) is the best, as it only attaches one event listener and will work with future elements added after the event is attached.
$(document).on('click', 'a.record', function() {
// Any `a` element with a class of `record` was clicked, now or in the future.
});
I've used document here, but you should use the nearest ancestor which won't change, which may be the ul element you have described.
I'm trying to build a greasemonkey script which will dynamically create tables of data based on user interaction with... other dynamically created tables of data. My problem is that I'm having to make two passes every time I create a table: one to create the table, and another to go grab all of the objects in the table I want to add event handlers to (by id) and add the various event handlers to them.
If I attempt to, say, add an onClick event to a table td before I've created the table and inserted it into the HTML, I get a "component is not available" exception.
This is incredibly cumbersome, because I either have to maintain, separately, a list of the ids and what I should do to those elements when I make my second pass to add the handlers, or develop a naming convention by which I know, based on the id, what I should do with the element.
There HAS to be a better way to do this. I just haven't figured it out yet. Anyone have any ideas?
Firstly, I'd love to know why you need a different ID for every single TD. Is the ID holding important information, such as an index? In this situation it might be better creating each TD within a loop. Also, obviously you can't attach an event handler to a DOM element which doesn't exist! It doesn't have to be injected into the DOM but it DOES have to exist in some capacity.
jQuery's live() isn't a magical mystery, it just uses event delegation, so it attaches the event to a parent element, such as the table and then decides what happens dependent on the target of the click. Here's a rudimentary example. I register a handler to the 'body' element, and then I test each time to see what the target is, if it's a TD element I doSomething() ->
document.body.onclick = function(e) {
var realTarget = e ? e.target : window.event.srcElement;
if ( realTarget.nodeName.toLowerCase() === 'td' ) {
doSomething();
}
};
Event delegation relies on something called event bubbling (or "propogation") which is the way in which modern browsers implement the event model. Each event, when triggered will travel upwards through the DOM until it can go no further. So if you click on an anchor within a paragraph the anchor's 'click' event will fire and THEN the paragraph's 'click' event will fire etc. etc.
jQuery 1.3+ has a new live() function that can set up event handlers for elements that don't exist yet .. check it out
You have to wait for the element to be added to the page, then add the event handler then.
There is no easy way to say "add this to all elements of this type, now and in the future".
It is possible to have a timer periodically check the page for new elements, applying a queue of events (or other properties) to them as they appear, all behind the scenes. This can be abstracted out and re-used, for example Jquery can do that sort of thing.
As JimmyP pointed out, your problem can easily be solved using event bubbling. You might consider writing a wrapper function to work around browser inconsistencies - my own version can be found here and would be used like this:
capture('click', '#element-id', function(event) {
// `this` will be the originating element
// return `false` to prevent default action
});