If I have 10 items, with the class name keyword:
<div class="keyword"></div>
How can I attach an event, for example click, on this element.
I tried the following, but with no luck: (no alert comes up)
document.getElementsByClassName('.keyword').onclick = function()
{
alert(true);
Search.addKey(this.getElementsByClassName('name')[0].innerHTML);
}
Requirements:
Without the onclick attribute
no jQuery or any other library
Note: the elements are not generated on page load. Their number can be different, each times you click a button for eg.
I need a way to attach to all tags with the class 'keyword' in the 'future'.
You should delegate the event. Try this:
if (document.body.addEventListener)
{
document.body.addEventListener('click',yourHandler,false);
}
else
{
document.body.attachEvent('onclick',yourHandler);//for IE
}
function yourHandler(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
if (target.className.match(/keyword/))
{
//an element with the keyword Class was clicked
}
}
You can read more on event delegation on quirksmode.com. AFAIK, this is the best way of achieving what you're trying to achieve. This is how all the major libs (prototype, jQuery, ...) work behind the scenes
Update:
Here's the fiddle, it contains some more explanation. An interesting reference is this page. It helped me understand the way IE and W3C events differ and, crucialy, it helped me understand the value, and countless benefits of Event Delegation
Related
I have a legacy code that has something like
<a href='#' onclick="javascript:Domystuff(); return false;"> Some text<a>
I add a jQuery for some other purpose in website, and now I want to detect if this given a tag has "onclick" event available or not. But I didn't found how can I do this. I see most suggest to use $("a").data('events') but that didn't work for me, and always give undefined. however when I call $("a").click() it does execute the Domystuff() function.
Any idea how can I detect if onclick or onchange or onblur like HTML attribute events are present or not.
EDIT: I forgot to mention that I don't just want to detect all events from attributes but I want all events on element for click or blur or change. Hope I make myself clear now. Thanks
if($('a').attr('onclick')) { // exists
}
after all, even eventhandlers are HTML - attributes.
Still, that won't detect eventhandlers attached by jQuery, so:
var ahrefs = $('a');
for(var i = 0;i < ahrefs.length;i++) {
var node = ahrefs.get(i);
if(typeof node.onClick == function) { // has an attached event handler
}
}
Easy, isn't it?
try something like this
if ($('a').attr("onClick") != undefined) {}
Is possible to add event listener (Javascript) to all dynamically generated elements?
I'm not the owner of the page, so I cannot add a listener in a static way.
For all the elements created when the page loaded I use:
doc.body.addEventListener('click', function(e){
//my code
},true);
I need a method to call this code when new elements appear on the page, but I cannot use jQuery (delegate, on, etc cannot work in my project). How can I do this?
It sounds like you need to pursue a delegation strategy without falling back to a library. I've posted some sample code in a Fiddle here: http://jsfiddle.net/founddrama/ggMUn/
The gist of it is to use the target on the event object to look for the elements you're interested in, and respond accordingly. Something like:
document.querySelector('body').addEventListener('click', function(event) {
if (event.target.tagName.toLowerCase() === 'li') {
// do your action on your 'li' or whatever it is you're listening for
}
});
CAVEATS! The example Fiddle only includes code for the standards-compliant browsers (i.e., IE9+, and pretty much every version of everyone else) If you need to support "old IE's" attachEvent, then you'll want to also provide your own custom wrapper around the proper native functions. (There are lots of good discussions out there about this; I like the solution Nicholas Zakas provides in his book Professional JavaScript for Web Developers.)
Depends on how you add new elements.
If you add using createElement, you can try this:
var btn = document.createElement("button");
btn.addEventListener('click', masterEventHandler, false);
document.body.appendChild(btn);
Then you can use masterEventHandler() to handle all clicks.
An obscure problem worth noting here may also be this fact I just discovered:
If an element has z-index set to -1 or smaller, you may think the
listener is not being bound, when in fact it is, but the browser
thinks you are clicking on a higher z-index element.
The problem, in this case, is not that the listener isn't bound, but instead it isn't able to get the focus, because something else (e.g., perhaps a hidden element) is on top of your element, and that what get's the focus instead (meaning: the event is not being triggered). Fortunately, you can detect this easily enough by right-clicking the element, selecting 'inspect' and checking to see if what you clicked on is what is being "inspected".
I am using Chrome, and I don't know if other browsers are so affected. But, it was hard to find because functionally, it resembles in most ways the problem with the listener not being bound. I fixed it by removing from CSS the line: z-index:-1;
When you have to support only "modern" web browsers (not Microsoft Internet Explorer), mutation observers are the right tool for this task:
new MutationObserver(function(mutationsList, observer) {
for(const mutation of mutationsList) {
if (mutation.type === 'childList') {
// put your own source code here
}
}
}).observe(document.body, {childList: true, subtree: true});
I have created a small function to add dynamic event listeners, similar to jQuery.on().
It uses the same idea as the accepted answer, only that it uses the Element.matches() method to check if the target matches the given selector string.
addDynamicEventListener(document.body, 'click', '.myClass, li', function (e) {
console.log('Clicked', e.target.innerText);
});
You can get if from github.
Delegating the anonymous task to dynamically created HTML elements with event.target.classList.contains('someClass')
returns true or false
eg.
let myEvnt = document.createElement('span');
myEvnt.setAttribute('class', 'someClass');
myEvnt.addEventListener('click', e => {
if(event.target.classList.contains('someClass')){
console.log(${event.currentTarget.classList})
}})
Reference: https://gomakethings.com/attaching-multiple-elements-to-a-single-event-listener-in-vanilla-js/
Good Read: https://eloquentjavascript.net/15_event.html#h_NEhx0cDpml
MDN : https://developer.mozilla.org/en-US/docs/Web/API/Event/Comparison_of_Event_Targets
Insertion Points:
You might wanna take a look at this library: https://github.com/Financial-Times/ftdomdelegate which is 1,8K gzipped
It is made for binding to events on all target elements matching the given selector, irrespective of whether anything exists in the DOM at registration time or not.
You need to import the script and then instantiate it like that:
var delegate = new Delegate(document.body);
delegate.on('click', 'button', handleButtonClicks);
// Listen to all touch move
// events that reach the body
delegate.on('touchmove', handleTouchMove);
});
Use classList property to bind more than one class at a time
var container = document.getElementById("table");
container.classList.add("row", "oddrow", "firstrow");
So I'm trying to use the .hover() method that only does something if the element being hovered over, DOES NOT have a particular class. It works fine if I add the class to the element in question in HTML. But say I have a click event that uses .addClass() to add the class using jQuery. The hover() method then ignores the class that's been added. Enough rambling. Here is the code.
$('.foo:not(.bar)').hover(function() {
someCrap(); //when hovering a over .foo that doesn't also have the class .bar, do someCrap
}
$('.foo').click(function(){
$(this).addClass('bar'); //after the element .foo gets another class .bar, the hover event written earlier continues to work, WTF
})
any idea guys and girls?
.hover() only adds events to elements that is already created. You need to use .on() for also creating events for future elements. This means that you need to set an parent also. Preferably the parent should be as close as possible to the children that should have the mouse events, to save performance.
$('#parent').on({
mouseenter : function() {
"use strict";
// Do something
},
mouseleave: function() {
"use strict";
// Do something
}
}, ".foo:not(.bar)");
When you run $(selector).hover(fn); any elements that match selector will have the event bound to them. If you were to add/change/remove something about the selector that later on makes that element no longer match, the event would not care because it was already bound.
There are a couple of ways you can handle this, and it really depends on the volume of JavaScript in your application and the specifics of your use case.
1) You could use event delegation, which takes advantage of the way that JavaScript events bubble to allow you to bind an event to a parent element of a particular part of your page and do logic at the time in which the event happens - this is very useful for a couple of reasons. One, imagine you have a table with 100 records and a link with a class of edit that should fire a particular JavaScript function when someone clicks on it. If you do $('a.edit').click(fn); jQuery will end up binding 100 different events to the individual elements. This can start to make your application struggle in older machines/browsers. Secondly, it makes it so that when things change dynamically (either you added a new row to the table or removed the edit class from a link because you no longer want to allow a particular row to be editable) your event is smart enough to know what is going on and still work. In jQuery, you use the .on() function for this. Read up on the documentation. If you have earlier versions of jQuery you might have to use .delegate() and if you are running some really old jQuery functions you are looking for .live().
2) A simpler approach if your application is relatively small is to simply bind the event to all .foo elements and check within the event itself if the element currently has a class of bar and proceed if it doesn't.
I'd say it would be more like:
$( '.foo' ).hover(
function() { if ( $( this ).has( '.bar' ) ) {} else { // do something } },
function() { if ( $( this ).has( '.bar' ) ) {} else { // do something } }
);
I'm a JS newbie - still learning. I'm looking for a solution similar to this example
for displaying the source link of an image in an alert using onclick. However, I want to apply it to any image on the page, and there are no ID's on any of the images. Perhaps this is an application of the mysterious 'this'? Can anyone help me? Thanks!
No, this has to do with delegate event listeners, and the way events spread across the DOM.
When you click on an element in the page, a click event is generated. For what it matters to your purposes, this event is fired on the element, and it's caught by the function you define with onclick.
But the event also "bubbles up" to the parent, and it's caught by the onclick function defined there, if any. And then to the parent of the parent, and so on.
What you have to do, now, is to catch the event on the root element, which is the document object itself, or maybe the document.body element if you still want to use onclick (which is deprecated).
The event object is passed to the onclick function and it contains the original element that fired the event:
document.body.onclick = function(e) {
var tgt = e.target || e.srcElement;
if (tgt.tagName === "IMG")
alert(tgt.src);
}
(The e.target || e.srcElement part is because in IE<9 the target property is called srcElement.) That's the way you define a delegate event listener. It's not defined on the <img> elements, as you can see, but on their common ancestor.
But since you can define just one click event listener in the traditional way, I'd strongly recommend to use something more modern like the addEventListener method, which lets you add multiple event listeners on the same element for the same event type:
document.body.addEventListener("click", function(e) {
...
});
In Internet Explorer <9 you'll have to use attachEvent, which is quite similar but not the same. For a cross-browser solution, use a common JS framework like jQuery, or Prototype, or whatever.
If I understand your question this is your solution,
var img = document.getElementsByTagName('img');
for(var i=0;i<img.length;i++){
alert(img[i].src);
}
Recently I was asked on a page with 10K+ items, how would you go about listening for click events on each item. I told him I would just bind a click to each item but looking at his face I could tell that wasn't the answer he was looking for. I'm having a hard time findinig any online articles regarding this type of use case which is why I'm not asking this on SO. It would be helpful if you could provide some sample code with your answer to help illustrate the solution.
You could use event delegation to do this...
var handleClick = function(e) {
// Older IEs set the `event` globally.
e = window.event || e;
// Older IEs use `srcElement` instead of the spec'd `target`.
var target = e.target || e.srcElement;
// For example's sake.
if (target.tagName.toLowerCase() == 'a' && target.className == 'some_class') {
// Handle this click.
}
}
if (document.addEventListener) {
document.addEventListener('click', handleClick, false);
} else (document.attachEvent) {
// Older IEs use `attachEvent` instead for adding event listeners.
document.attachEvent('onclick', handleClick);
} else {
// When all else fails, let's take a journey back to the 90's.
document.onclick = handleClick;
}
Event delegation works by capturing the event which bubbles up through all ancestor elements by default.
You can (and should) replace document with the closest consistent ancestor element.
The code example above will support all browsers that you probably care about.
If using a library such as jQuery, the code is generally more terse...
$(document).on('click', 'a.some_class', function() {
// Handle this click.
});
You should bind a single listener to the container of the items, a technique called event delegation. This works because in javascript events bubble up through the all the node's parents to the root node. When you catch the event you need to do a quick check to make sure the click was on one of the node types you want to listen to.