Bind event to element using pure Javascript - javascript

I'm writing an API and unfortunately need to avoid any jQuery or 3rd party libraries. How exactly are the events bound to elements through jQuery?
jQuery example:
$('#anchor').click(function(){
console.log('anchor');
});
I'm close to writing a onClick attribute on the element out of frustration, but I would like to understand the link between the jQuery event and DOM element.
How exactly does the element know to fire a jQuery event when the markup itself is not changed? How are they catching the DOM event and overriding it?
I have created my own Observer handler but cant quite understand how to link the element to the Observer events.

Here's a quick answer:
document.getElementById('anchor').addEventListener('click', function() {
console.log('anchor');
});
Every modern browser supports an entire API for interacting with the DOM through JavaScript without having to modify your HTML. See here for a pretty good bird's eye view: http://overapi.com/javascript

You identify the element by id, in this case anchor, by:
var el = document.getElementById('anchor');
Then you need to assign your click event:
el[window.addEventListener ? 'addEventListener' : 'attachEvent']( window.addEventListener ? 'click' : 'onclick', myClickFunc, false);
And finally, the event's function would be something like:
function myClickFunc()
{
console.log('anchor');
}
You could simplify it or turn it into a one-liner if you do not need compatibility with older browsers and and a range of browsers, but jQuery does cross-browser compatibility and does its best to give you the functionality you are looking for in as many older browsers as it can.

Related

How can I create multiple click events for the same element [duplicate]

What's the difference between addEventListener and onclick?
var h = document.getElementById("a");
h.onclick = dothing1;
h.addEventListener("click", dothing2);
The code above resides together in a separate .js file, and they both work perfectly.
Both are correct, but none of them are "best" per se, and there may be a reason the developer chose to use both approaches.
Event Listeners (addEventListener and IE's attachEvent)
Earlier versions of Internet Explorer implement JavaScript differently from pretty much every other browser. With versions less than 9, you use the attachEvent[doc] method, like this:
element.attachEvent('onclick', function() { /* do stuff here*/ });
In most other browsers (including IE 9 and above), you use addEventListener[doc], like this:
element.addEventListener('click', function() { /* do stuff here*/ }, false);
Using this approach (DOM Level 2 events), you can attach a theoretically unlimited number of events to any single element. The only practical limitation is client-side memory and other performance concerns, which are different for each browser.
The examples above represent using an anonymous function[doc]. You can also add an event listener using a function reference[doc] or a closure[doc]:
var myFunctionReference = function() { /* do stuff here*/ }
element.attachEvent('onclick', myFunctionReference);
element.addEventListener('click', myFunctionReference , false);
Another important feature of addEventListener is the final parameter, which controls how the listener reacts to bubbling events[doc]. I've been passing false in the examples, which is standard for probably 95% of use cases. There is no equivalent argument for attachEvent, or when using inline events.
Inline events (HTML onclick="" property and element.onclick)
In all browsers that support javascript, you can put an event listener inline, meaning right in the HTML code. You've probably seen this:
<a id="testing" href="#" onclick="alert('did stuff inline');">Click me</a>
Most experienced developers shun this method, but it does get the job done; it is simple and direct. You may not use closures or anonymous functions here (though the handler itself is an anonymous function of sorts), and your control of scope is limited.
The other method you mention:
element.onclick = function () { /*do stuff here */ };
... is the equivalent of inline javascript except that you have more control of the scope (since you're writing a script rather than HTML) and can use anonymous functions, function references, and/or closures.
The significant drawback with inline events is that unlike event listeners described above, you may only have one inline event assigned. Inline events are stored as an attribute/property of the element[doc], meaning that it can be overwritten.
Using the example <a> from the HTML above:
var element = document.getElementById('testing');
element.onclick = function () { alert('did stuff #1'); };
element.onclick = function () { alert('did stuff #2'); };
... when you clicked the element, you'd only see "Did stuff #2" - you overwrote the first assigned of the onclick property with the second value, and you overwrote the original inline HTML onclick property too. Check it out here: http://jsfiddle.net/jpgah/.
Broadly speaking, do not use inline events. There may be specific use cases for it, but if you are not 100% sure you have that use case, then you do not and should not use inline events.
Modern Javascript (Angular and the like)
Since this answer was originally posted, javascript frameworks like Angular have become far more popular. You will see code like this in an Angular template:
<button (click)="doSomething()">Do Something</button>
This looks like an inline event, but it isn't. This type of template will be transpiled into more complex code which uses event listeners behind the scenes. Everything I've written about events here still applies, but you are removed from the nitty gritty by at least one layer. You should understand the nuts and bolts, but if your modern JS framework best practices involve writing this kind of code in a template, don't feel like you're using an inline event -- you aren't.
Which is Best?
The question is a matter of browser compatibility and necessity. Do you need to attach more than one event to an element? Will you in the future? Odds are, you will. attachEvent and addEventListener are necessary. If not, an inline event may seem like they'd do the trick, but you're much better served preparing for a future that, though it may seem unlikely, is predictable at least. There is a chance you'll have to move to JS-based event listeners, so you may as well just start there. Don't use inline events.
jQuery and other javascript frameworks encapsulate the different browser implementations of DOM level 2 events in generic models so you can write cross-browser compliant code without having to worry about IE's history as a rebel. Same code with jQuery, all cross-browser and ready to rock:
$(element).on('click', function () { /* do stuff */ });
Don't run out and get a framework just for this one thing, though. You can easily roll your own little utility to take care of the older browsers:
function addEvent(element, evnt, funct){
if (element.attachEvent)
return element.attachEvent('on'+evnt, funct);
else
return element.addEventListener(evnt, funct, false);
}
// example
addEvent(
document.getElementById('myElement'),
'click',
function () { alert('hi!'); }
);
Try it: http://jsfiddle.net/bmArj/
Taking all of that into consideration, unless the script you're looking at took the browser differences into account some other way (in code not shown in your question), the part using addEventListener would not work in IE versions less than 9.
Documentation and Related Reading
W3 HTML specification, element Event Handler Attributes
element.addEventListener on MDN
element.attachEvent on MSDN
Jquery.on
quirksmode blog "Introduction to Events"
CDN-hosted javascript libraries at Google
The difference you could see if you had another couple of functions:
var h = document.getElementById('a');
h.onclick = doThing_1;
h.onclick = doThing_2;
h.addEventListener('click', doThing_3);
h.addEventListener('click', doThing_4);
Functions 2, 3 and 4 work, but 1 does not. This is because addEventListener does not overwrite existing event handlers, whereas onclick overrides any existing onclick = fn event handlers.
The other significant difference, of course, is that onclick will always work, whereas addEventListener does not work in Internet Explorer before version 9. You can use the analogous attachEvent (which has slightly different syntax) in IE <9.
In this answer I will describe the three methods of defining DOM event handlers.
element.addEventListener()
Code example:
const element = document.querySelector('a');
element.addEventListener('click', event => event.preventDefault(), true);
Try clicking this link.
element.addEventListener() has multiple advantages:
Allows you to register unlimited events handlers and remove them with element.removeEventListener().
Has useCapture parameter, which indicates whether you'd like to handle event in its capturing or bubbling phase. See: Unable to understand useCapture attribute in addEventListener.
Cares about semantics. Basically, it makes registering event handlers more explicit. For a beginner, a function call makes it obvious that something happens, whereas assigning event to some property of DOM element is at least not intuitive.
Allows you to separate document structure (HTML) and logic (JavaScript). In tiny web applications it may not seem to matter, but it does matter with any bigger project. It's way much easier to maintain a project which separates structure and logic than a project which doesn't.
Eliminates confusion with correct event names. Due to using inline event listeners or assigning event listeners to .onevent properties of DOM elements, lots of inexperienced JavaScript programmers thinks that the event name is for example onclick or onload. on is not a part of event name. Correct event names are click and load, and that's how event names are passed to .addEventListener().
Works in almost all browser. If you still have to support IE <= 8, you can use a polyfill from MDN.
element.onevent = function() {} (e.g. onclick, onload)
Code example:
const element = document.querySelector('a');
element.onclick = event => event.preventDefault();
Try clicking this link.
This was a way to register event handlers in DOM 0. It's now discouraged, because it:
Allows you to register only one event handler. Also removing the assigned handler is not intuitive, because to remove event handler assigned using this method, you have to revert onevent property back to its initial state (i.e. null).
Doesn't respond to errors appropriately. For example, if you by mistake assign a string to window.onload, for example: window.onload = "test";, it won't throw any errors. Your code wouldn't work and it would be really hard to find out why. .addEventListener() however, would throw error (at least in Firefox): TypeError: Argument 2 of EventTarget.addEventListener is not an object.
Doesn't provide a way to choose if you want to handle event in its capturing or bubbling phase.
Inline event handlers (onevent HTML attribute)
Code example:
Try clicking this link.
Similarly to element.onevent, it's now discouraged. Besides the issues that element.onevent has, it:
Is a potential security issue, because it makes XSS much more harmful. Nowadays websites should send proper Content-Security-Policy HTTP header to block inline scripts and allow external scripts only from trusted domains. See How does Content Security Policy work?
Doesn't separate document structure and logic.
If you generate your page with a server-side script, and for example you generate a hundred links, each with the same inline event handler, your code would be much longer than if the event handler was defined only once. That means the client would have to download more content, and in result your website would be slower.
See also
EventTarget.addEventListener() documentation (MDN)
EventTarget.removeEventListener() documentation (MDN)
onclick vs addEventListener
dom-events tag wiki
While onclick works in all browsers, addEventListener does not work in older versions of Internet Explorer, which uses attachEvent instead.
The downside of onclick is that there can only be one event handler, while the other two will fire all registered callbacks.
Summary:
addEventListener can add multiple events, whereas with onclick this cannot be done.
onclick can be added as an HTML attribute, whereas an addEventListener can only be added within <script> elements.
addEventListener can take a third argument which can stop the event propagation.
Both can be used to handle events. However, addEventListener should be the preferred choice since it can do everything onclick does and more. Don't use inline onclick as HTML attributes as this mixes up the javascript and the HTML which is a bad practice. It makes the code less maintainable.
As far as I know, the DOM "load" event still does only work very limited. That means it'll only fire for the window object, images and <script> elements for instance. The same goes for the direct onload assignment. There is no technical difference between those two. Probably .onload = has a better cross-browser availabilty.
However, you cannot assign a load event to a <div> or <span> element or whatnot.
An element can have only one event handler attached per event type, but can have multiple event listeners.
So, how does it look in action?
Only the last event handler assigned gets run:
const button = document.querySelector(".btn")
button.onclick = () => {
console.log("Hello World");
};
button.onclick = () => {
console.log("How are you?");
};
button.click() // "How are you?"
All event listeners will be triggered:
const button = document.querySelector(".btn")
button.addEventListener("click", event => {
console.log("Hello World");
})
button.addEventListener("click", event => {
console.log("How are you?");
})
button.click()
// "Hello World"
// "How are you?"
IE Note: attachEvent is no longer supported. Starting with IE 11, use addEventListener: docs.
One detail hasn't been noted yet: modern desktop browsers consider different button presses to be "clicks" for AddEventListener('click' and onclick by default.
On Chrome 42 and IE11, both onclick and AddEventListener click fire on left and middle click.
On Firefox 38, onclick fires only on left click, but AddEventListener click fires on left, middle and right clicks.
Also, middle-click behavior is very inconsistent across browsers when scroll cursors are involved:
On Firefox, middle-click events always fire.
On Chrome, they won't fire if the middleclick opens or closes a scroll cursor.
On IE, they fire when scroll cursor closes, but not when it opens.
It is also worth noting that "click" events for any keyboard-selectable HTML element such as input also fire on space or enter when the element is selected.
element.onclick = function() { /* do stuff */ }
element.addEventListener('click', function(){ /* do stuff */ },false);
They apparently do the same thing: listen for the click event and execute a callback function. Nevertheless, they’re not equivalent. If you ever need to choose between the two, this could help you to figure out which one is the best for you.
The main difference is that onclick is just a property, and like all object properties, if you write on more than once, it will be overwritten. With addEventListener() instead, we can simply bind an event handler to the element, and we can call it each time we need it without being worried of any overwritten properties.
Example is shown here,
Try it: https://jsfiddle.net/fjets5z4/5/
In first place I was tempted to keep using onclick, because it’s shorter and looks simpler… and in fact it is. But I don’t recommend using it anymore. It’s just like using inline JavaScript. Using something like – that’s inline JavaScript – is highly discouraged nowadays (inline CSS is discouraged too, but that’s another topic).
However, the addEventListener() function, despite it’s the standard, just doesn’t work in old browsers (Internet Explorer below version 9), and this is another big difference. If you need to support these ancient browsers, you should follow the onclick way. But you could also use jQuery (or one of its alternatives): it basically simplifies your work and reduces the differences between browsers, therefore can save you a lot of time.
var clickEvent = document.getElementByID("onclick-eg");
var EventListener = document.getElementByID("addEventListener-eg");
clickEvent.onclick = function(){
window.alert("1 is not called")
}
clickEvent.onclick = function(){
window.alert("1 is not called, 2 is called")
}
EventListener.addEventListener("click",function(){
window.alert("1 is called")
})
EventListener.addEventListener("click",function(){
window.alert("2 is also called")
})
Javascript tends to blend everything into objects and that can make it confusing. All into one is the JavaScript way.
Essentially onclick is a HTML attribute. Conversely addEventListener is a method on the DOM object representing a HTML element.
In JavaScript objects, a method is merely a property that has a function as a value and that works against the object it is attached to (using this for example).
In JavaScript as HTML element represented by DOM will have it's attributes mapped onto its properties.
This is where people get confused because JavaScript melds everything into a single container or namespace with no layer of indirection.
In a normal OO layout (which does at least merge the namespace of properties/methods) you would might have something like:
domElement.addEventListener // Object(Method)
domElement.attributes.onload // Object(Property(Object(Property(String))))
There are variations like it could use a getter/setter for onload or HashMap for attributes but ultimately that's how it would look. JavaScript eliminated that layer of indirection at the expect of knowing what's what among other things. It merged domElement and attributes together.
Barring compatibility you should as a best practice use addEventListener. As other answers talk about the differences in that regard rather than the fundamental programmatic differences I will forgo it. Essentially, in an ideal world you're really only meant to use on* from HTML but in an even more ideal world you shouldn't be doing anything like that from HTML.
Why is it dominant today? It's quicker to write, easier to learn and tends to just work.
The whole point of onload in HTML is to give access to the addEventListener method or functionality in the first place. By using it in JS you're going through HTML when you could be applying it directly.
Hypothetically you can make your own attributes:
$('[myclick]').each(function(i, v) {
v.addEventListener('click', function() {
eval(v.myclick); // eval($(v).attr('myclick'));
});
});
What JS does with is a bit different to that.
You can equate it to something like (for every element created):
element.addEventListener('click', function() {
switch(typeof element.onclick) {
case 'string':eval(element.onclick);break;
case 'function':element.onclick();break;
}
});
The actual implementation details will likely differ with a range of subtle variations making the two slightly different in some cases but that's the gist of it.
It's arguably a compatibility hack that you can pin a function to an on attribute since by default attributes are all strings.
You should also consider EventDelegation for that!
For that reason I prefer the addEventListener and foremost using it carefully and consciously!
FACTS:
EventListeners are heavy .... (memory allocation at the client side)
The Events propagate IN and then OUT again in relation to the DOM
tree. Also known as trickling-in and bubbling-out , give it a read
in case you don't know.
So imagine an easy example:
a simple button INSIDE a div INSIDE body ...
if you click on the button, an Event will ANYWAY
trickle in to BUTTON and then OUT again, like this:
window-document-div-button-div-document-window
In the browser background (lets say the software periphery of the JS engine) the browser can ONLY possibly react to a click, if it checks for each click done where it was targeted.
And to make sure that each possible event listener on the way is triggered, it kinda has to send the "click event signal" all the way from document level down into the element ... and back out again.
This behavior can then made use of by attaching EventListeners using e.g.:
document.getElementById("exampleID").addEventListener("click",(event) => {doThis}, true/false);
Just note for reference that the true/false as the last argument of the addEventListener method controls the behavior in terms of when is the event recognized - when trickling in or when bubbling out.
TRUE means, the event is recognized while trickling-in
FALSE means, the event is recognized on its way bubbling out
Implementing the following 2 helpful concepts also turns out much more intuitive using the above stated approach to handle:
You can also use event.stopPropagation() within the function
(example ref. "doThis") to prevents further propagation of the
current event in the capturing and bubbling phases. It does not,
however, prevent any default behaviors from occurring; for instance,
clicks on links are still processed.
If you want to stop those behaviors, you could use
event.preventDefault() within the function (example ref.
"doThis"). With that you could for example tell the Browser that if
the event does not get explicitly handled, its default action should
not be taken as it normally would be.
Also just note here for reference again: the last argument of the addEventListener method (true/false) also controls at which phase (trickling-in TRUE or bubbling out FALSE) the eventual effect of ".stopPropagation()" kicks in.
So ... in case you apply an EventListener with flag TRUE to an element, and combine that with the .stopPropagation() method, the event would not even get through to potential inner children of the element
To wrap it up:
If you use the onClick variant in HTML ... there are 2 downsides for me:
With addEventListener, you can attach multiple onClick-events to the same, respectively one single element, but thats not possible using onClick (at least thats what I strongly believe up to now, correct me if I am wrong).
Also the following aspect is truly remarkable here ... especially the code maintenance part (didn't elaborate on this so far):
In regards to event delegation, it really boils down to this. If some
other JavaScript code needs to respond to a click event, using
addEventListener ensures you both can respond to it. If you both try
using onclick, then one stomps on the other. You both can't respond if
you want an onclick on the same element. Furthermore, you want to keep your behavior as separate as you can from the HTML in case you need to change it later. It would suck to have 50 HTML files to update instead of one JavaScript file.
(credit to Greg Burghardt, addEventListener vs onclick with regards to event delegation )
This is also known by the term "Unobtrusive JavaScript" ... give it a read!
According to MDN, the difference is as below:
addEventListener:
The EventTarget.addEventListener() method adds the specified
EventListener-compatible object to the list of event listeners for the
specified event type on the EventTarget on which it's called. The
event target may be an Element in a document, the Document itself, a
Window, or any other object that supports events (such as
XMLHttpRequest).
onclick:
The onclick property returns the click event handler code on the
current element. When using the click event to trigger an action, also
consider adding this same action to the keydown event, to allow the
use of that same action by people who don't use a mouse or a touch
screen. Syntax element.onclick = functionRef; where functionRef is a
function - often a name of a function declared elsewhere or a function
expression. See "JavaScript Guide:Functions" for details.
There is also a syntax difference in use as you see in the below codes:
addEventListener:
// Function to change the content of t2
function modifyText() {
var t2 = document.getElementById("t2");
if (t2.firstChild.nodeValue == "three") {
t2.firstChild.nodeValue = "two";
} else {
t2.firstChild.nodeValue = "three";
}
}
// add event listener to table
var el = document.getElementById("outside");
el.addEventListener("click", modifyText, false);
onclick:
function initElement() {
var p = document.getElementById("foo");
// NOTE: showAlert(); or showAlert(param); will NOT work here.
// Must be a reference to a function name, not a function call.
p.onclick = showAlert;
};
function showAlert(event) {
alert("onclick Event detected!");
}
I guess Chris Baker pretty much summed it up in an excellent answer but I would like to add to that with addEventListener() you can also use options parameter which gives you more control over your events. For example - If you just want to run your event once then you can use { once: true } as an option parameter when adding your event to only call it once.
function greet() {
console.log("Hello");
}
document.querySelector("button").addEventListener('click', greet, { once: true })
The above function will only print "Hello" once.
Also, if you want to cleanup your events then there is also the option to removeEventListener(). Although there are advantages of using addEventListener() but you should still be careful if your targeting audience is using Internet Explorer then this method might not work in all situation. You can also read about addEventListener on MDN, they gave quite a good explanation on how to use them.
If you are not too worried about browser support, there is a way to rebind the 'this' reference in the function called by the event. It will normally point to the element that generated the event when the function is executed, which is not always what you want. The tricky part is to at the same time be able to remove the very same event listener, as shown in this example: http://jsfiddle.net/roenbaeck/vBYu3/
/*
Testing that the function returned from bind is rereferenceable,
such that it can be added and removed as an event listener.
*/
function MyImportantCalloutToYou(message, otherMessage) {
// the following is necessary as calling bind again does
// not return the same function, so instead we replace the
// original function with the one bound to this instance
this.swap = this.swap.bind(this);
this.element = document.createElement('div');
this.element.addEventListener('click', this.swap, false);
document.body.appendChild(this.element);
}
MyImportantCalloutToYou.prototype = {
element: null,
swap: function() {
// now this function can be properly removed
this.element.removeEventListener('click', this.swap, false);
}
}
The code above works well in Chrome, and there's probably some shim around making "bind" compatible with other browsers.
Using inline handlers is incompatible with Content Security Policy so the addEventListener approach is more secure from that point of view. Of course you can enable the inline handlers with unsafe-inline but, as the name suggests, it's not safe as it brings back the whole hordes of JavaScript exploits that CSP prevents.
It should also be possible to either extend the listener by prototyping it (if we have a reference to it and its not an anonymous function) -or make the onclick call a call to a function library (a function calling other functions).
Like:
elm.onclick = myFunctionList;
function myFunctionList(){
myFunc1();
myFunc2();
}
This means we never have to change the onclick call just alter the function myFunctionList() to do whatever we want, but this leaves us without control of bubbling/catching phases so should be avoided for newer browsers.
addEventListener lets you set multiple handlers, but isn't supported in IE8 or lower.
IE does have attachEvent, but it's not exactly the same.
The context referenced by 'this' keyword in JavasSript is different.
look at the following code:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<input id="btnSubmit" type="button" value="Submit" />
<script>
function disable() {
this.disabled = true;
}
var btnSubmit = document.getElementById('btnSubmit');
btnSubmit.onclick = disable();
//btnSubmit.addEventListener('click', disable, false);
</script>
</body>
</html>
What it does is really simple. when you click the button, the button will be disabled automatically.
First when you try to hook up the events in this way button.onclick = function(),
onclick event will be triggered by clicking the button, however, the button will not be disabled because there's no explicit binding between button.onclick and onclick event handler. If you debug see the 'this' object, you can see it refers to 'window' object.
Secondly, if you comment btnSubmit.onclick = disable(); and uncomment
//btnSubmit.addEventListener('click', disable, false); you can see that the button is disabled because with this way there's explicit binding between button.onclick event and onclick event handler. If you debug into disable function, you can see 'this' refers to the button control rather than the window.
This is something I don't like about JavaScript which is inconsistency.
Btw, if you are using jQuery($('#btnSubmit').on('click', disable);), it uses explicit binding.
onclick is basically an addEventListener that specifically performs a function when the element is clicked. So, useful when you have a button that does simple operations, like a calculator button. addEventlistener can be used for a multitude of things like performing an operation when DOM or all content is loaded, akin to window.onload but with more control.
Note, You can actually use more than one event with inline, or at least by using onclick by seperating each function with a semi-colon, like this....
I wouldn't write a function with inline, as you could potentially have problems later and it would be messy imo. Just use it to call functions already done in your script file.
Which one you use I suppose would depend on what you want. addEventListener for complex operations and onclick for simple. I've seen some projects not attach a specific one to elements and would instead implement a more global eventlistener that would determine if a tap was on a button and perform certain tasks depending on what was pressed. Imo that could potentially lead to problems I'd think, and albeit small, probably, a resource waste if that eventlistener had to handle each and every click
in my Visual Studio Code, addEventListener has Real Intellisense on event
but onclick does not, only fake ones
let element = document.queryselector('id or classname');
element.addeventlistiner('click',()=>{
do work
})
<button onclick="click()">click</click>`
function click(){
do work
};

Can we name an event handler in Javascript?

I was messing around with jQuery and event handlers, when I noticed this:
That uses jQuery, and without it:
How does the popup get a bar saying jQuery? Do browsers have integrated jQuery support to detect that? Or is there some way to name event handlers? I want to have my event display some other text, like how jQuery does.
NOTE: I don't want to use jQuery, as I want to know how jQuery does it.
I am not sure about what you are trying to do, but in Javascript you can always name functions
Example:
function myEventHandler(event) {
alert('event handler');
}
myEventHandler is the name of the function.
Hope this helps a little,
best,
Carsten
You can have custom names for your events if you want. We can use the trigger function for the same purpose.
Suppose you want to raise myEvent on <div id="my_div">.
We can simply
$("#my_div").trigger('myEvent');
and have a listener for the event:
$("#my_div").on('myEvent', function(event){
//Event handler
});
You can find some good documentation here -
https://learn.jquery.com/events/introduction-to-custom-events/
And this SO answer covers it thoroughly -
Custom events in jQuery?

add event listener on elements created dynamically

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");

How can I add onLoad events to elements with unknown names at document load using jQuery?

I'm using a third-party commenting plugin, and I would like to change the content of some of the buttons. This is straightforward for buttons with id's known ahead of time, but it also has buttons that don't appear until a 'Reply' button is clicked. To be clear, these elements are not present when the page is loaded. They are inserted into the DOM following some event. For those elements, I only know a prefix of the id.
My first thought was to use .on, and to delegate to the children of the reply container, but the load event does not bubble, so this doesn't work:
<script>
$("#container").on("load", 'a[id|="reply-button"]', function(event) { $(this).html("different text"); } );
</script>
<div id="container">
<a id="reply-button-42das56ds6d78a">some text</a>
</div>
What's the next best thing?
"I know they will appear when the 'Reply' button is clicked. When that happens, new elements are inserted into the DOM, and I know what the prefix of the id of those elements will be."
You could use something like the DOMSubtreeModified event to tell when elements are added, but that isn't supported by all browsers. (In fact it has been deprecated.)
Or you could attach a click handler to the 'Reply' button:
$(document).ready(function() {
// initialise plugin here, then:
$("some selector for the reply button(s)").click(function(e) {
// setTimeout(function() {
$('a[id|="reply-button"]').html("different text");
// }, 10);
});
});
jQuery ensures that multiple event handlers will run in the order they are bound, but of course this only applies to handlers added with jQuery. So if the third-party commenting plugin you are using also uses jQuery then just be sure it is initialised first and your own reply click handler should run afterwards and at that time it will be able to access the elements added by the plugin.
If the plugin doesn't use jQuery you can't be sure your click handler will run last so instead uncomment the setTimeout code I've shown above - it will wait a few milliseconds to give the plugin events time to run and then update the text.
Use the selector $('#^=id')
id being the prefix
e.g all ids starting test123
$('#^=test123')
this would work for things like
test1234
test12345
test123fgjfdgj
This might help: http://oscarotero.com/jquery/
And use jquery event listeners for the page load..
e.g. $(document).ready(function(){});
If they are loaded when a button is clicked then do..
$('#buttonid').click(function() {//handle click});
You're looking for DOM Mutation Events. This spec allows you to be notified when DOM nodes are inserted, changed, etc. Browser support is not really there, though... well, IE is behind (IE >= 9 has support). It's also a major performance hog. See this MDN document. For these reasons, I don't think a lot of folks here would suggest using them. Here's some code, though:
document.addEventListener("DOMNodeInserted", function(e) {
if ($(e.target).is('selector matching new elements')) {
//do what you want with e.target, which is the newly-inserted element
}
}, false);
There is a performance-boosting hack involving listening for CSS animation events instead: here. Only problem is that IE9 does not support CSS animations. I think we're all waiting for the day when we can use these events in a cross-browser and performant way, though.

Adding Javascript EventListener only if no other listener is present?

I'm a relative tyro in the Javascript programming arena, so please go easy on me. :-)
I am trying to add an eventListener to various DOM elements on a page. However, some of the elements I'm adding my event to already have an eventListener, and I don't want to supersede that one; I only want to add my event to an element that doesn't already have an event associated with it.
I've looked through a bunch of stuff relating to addEventListener, event.StopPropagation, event bubbling, and so forth, but haven't figured out any way to accomplish this yet.
Is there a way to detect other event listeners on a given element, or some other way to get where I want?
You can check if the on[event] property of that given element is set by using:
if (typeof(document.getElementById("element-id").onclick) == "undefined") {
// event undefined
}
if (typeof(document.getElementById("element-id").onclick) == "function") {
// event defined
}
Notice that this won't work if a javascript library such as jQuery were used to define the event (e.g. by using $("#element-id").click()). I'd recommend you to use jQuery, you can handle events easily with it.
edit: uh, well, afaik it doesn't work if you're using addEventHandler too. It only works if you set your event by using yourElement.onclick = anyFunction

Categories

Resources