How to apply live() like feature for JavaScript appended DOM elements - javascript

How to apply live() like feature for JavaScript appended DOM elements?
Like a li list inside ul which is added through JavaScript. I need to do this in plain JavaScript.

Since .live() is simply event delegation, place your handler on the nearest element to the ones being added.
var container = document.getElementById('my_container');
container.onclick = function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
while(target && target.nodeName.toUpperCase() !== 'LI' ) {
if( target === this )
target = null;
else
target = target.parentNode;
}
if( target ) {
// work with the LI
}
};
This is also similar to .live() in the sense that it searches from the e.target up to the container with the delegate to see if it is your targeted element.
Just testing the e.target itself isn't enough if the li has descendants.
For more complex analysis of the elements, you could use .matchesSelector, though you'd need to stick it on the HTMLElement.prototype under the correct name, since most browsers include it as an extension.
Also, you'd need a patch for IE8, but that's pretty easy.
if (HTMLElement) {
if (!HTMLElement.prototype.matches && !HTMLElement.prototype.matchesSelector) {
HTMLElement.prototype.matches =
HTMLELement.prototype.matchesSelector =
HTMLElement.prototype.webkitMatchesSelector ||
HTMLElement.prototype.mozMatchesSelecvtor ||
HTMLElement.prototype.msMatchesSelector ||
HTMLElement.prototype.oMatchesSelector;
}
} else if (!Element.prototype.matchesSelector && Element.prototype.querySelectorAll) {
Element.prototype.matches =
Element.prototype.matchesSelector =
function() {
// exercise for reader to implement using .querySelectorAll,
// though it's pretty easy, and available online if you search
}
}

You have to bind an event to the document root, and check the event.target property. If it matches the given selector, then do something.
Example (assuming addEventListener)
Example: Match all elements with id test:
var root = document.documentElement;
root.addEventListener('click', function(event) {
var target = event.target; // <-- Clicked element
while (target && target !== root) { // Tree traversing
if (target.id == 'test') { // <------ Matches selector
// Do something.
break; // Optional: Stop traversal, because a match has been found
}
target = target.parentNode; // Go up in the tree
}
}, true);

the live() is a function of jquery library
.live( events, handler(eventObject) )
events: A string containing a JavaScript event type, such as "click" or "keydown."
As of jQuery 1.4 the string can contain multiple, space-separated event types or custom event names.
handler(eventObject): A function to execute at the time the event is triggered.
for your example, when you created the li inside the ul, you have to you live to capture the li,e.g,
$('li').live('mouseover',function(){
alert('hello');
});

You can manually attach the event handler whenever you create a new element. Or, you can do it exactly how jQuery is doing it by looking into the jQuery library and extracting the parts you need.

Related

Event Delegation / Attaching events to dynamically created elements using Vanilla JavaScript

I am in the process of converting a large script from jQuery to JavaScript. This was code that I didn't write myself but that I forked from a project on GitHub.
I've consulted W3Schools, the official documentation and this website as a reference.
http://youmightnotneedjquery.com/
One of the parts I'm trying to convert into JavaScript is the following.
$('body').on('click','.vb',function(){
exportVB(this.value);
});
According to the aforementioned link,
$(document).on(eventName, elementSelector, handler);
converts to this
document.addEventListener(eventName, function(e) {
// loop parent nodes from the target to the delegation node
for (var target = e.target; target && target != this; target = target.parentNode) {
if (target.matches(elementSelector)) {
handler.call(target, e);
break;
}
}
}, false);
My attempt is as follows
/*document.addEventListener('click',function(e) {
for (var target = e.target; target && target != this; target = target.parentNode) {
if (target.matches('.vb')) {
exportVB.call(target,e);
break;
}
}
}, false);*/
That evidently didn't work so I did a Google search that brought me to this StackOverflow solution
Attach event to dynamic elements in javascript
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'brnPrepend'){
//do something
}
});
//$(document).on('click','#btnPrepend',function(){//do something})
Testing that gave me this idea. I commented it out because that apparently didn't work either.
/*document.addEventListener('click',function(e) {
if (e.target && e.target.className == 'vb') {
exportVB(this.value);
}
});*/
Just for reference, the original jQuery function works well.
I solved it.
document.body.addEventListener('click',function(e) {
for (var target = e.target; target && target != this; target = target.parentNode) {
if (target.matches('.vb')) {
exportVB(target.value);
break;
}
}
});
I can't explain how it worked because I didn't write the original code in the first place. But there were two things I change.
exportVB.call(target.e) to exportVB(target.value)
Removing the false as the last argument.
Rather than iterating over each parent element manually, consider using .closest instead, which will return the ancestor element (or the current element) which matches a selector:
document.querySelector('button').addEventListener('click', () => {
document.body.insertAdjacentHTML('beforeend', '<span class="vb">some span </span>');
});
document.body.addEventListener('click', (e) => {
if (e.target.closest('.vb')) {
console.log('vb clicked');
}
});
<button>add span</button>

Detect focus on an input (onfocusin) even id the element does not exists in the dom yet with vanilla (plain) javascript

I have the following:
document.querySelectorAll("input[type='text']").forEach((element) => {
element.addEventListener('onfocusin', () => {
alert("was focused!!!");
});
});
The problem with these is that only works when the element exists. If some element is added to the dom after this code is called, it will not be added any event listener.
How can I detect the onfocusin event even if the element does not exist yet?
Put the event listener on a parent element that contains all (future) inputs (for example, the document itself). The event will "bubble up" from the input. Inside the handler you will need to check if the target is actually the element you want:
document.addEventListener('focusin', (e) => {
const target = e.target;
if (target.tagName == "INPUT" && target.type == "text") {
alert("was focused!!!");
}
});
MutationObserver is supported by most modern browsers, you can hook the dom changed event therein
var observeDOM = (function(){
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
return function( obj, callback ){
if( !obj || !obj.nodeType === 1 ) return;
if( MutationObserver ){
// define a new observer
var obs = new MutationObserver(function(mutations, observer){
callback(mutations);
})
obs.observe( obj, { childList:true, subtree:true });
}
else if( window.addEventListener ){
obj.addEventListener('DOMNodeInserted', callback, false); //<----------****
obj.addEventListener('DOMNodeRemoved', callback, false);
}
}
})();
You can add your own logic in DOMNodeInserted() function:
Do you have access to jQuery or does it have to be plain Javascript?
In case you can use jQuery you can do:
$(document).on("focusin","input[type='text']",function(event) {
alert('element was focused', {event:event});
// let element = $(event.currentTarget);
});
See RoToRa's answer for plain JS
Angular/Typescript version
Based on the #RoToRa's answer and because I want to use this in an angular/typescript application, here how to use the #RotoRa's code in typescript:
document.addEventListener('focusin', (event: Event) => {
const target: HTMLElement = event.target as HTMLElement;
if (target instanceof HTMLInputElement && target.type === 'text') {
console.log("element was focused)
}
});

Javascript document event on certain css class

I'm used to writing in jQuery for selecting by class, however the following I can't quite get the code right. This lives on every page and should just intercept links with the class 'download-link'. The following works for all links. But i want to target it just for download-link css.
document.onclick = function (e) {
e = e || window.event;
var element = e.target || e.srcElement;
if (element.tagName == 'A') {
window.open(element.href, "_blank", "location=yes,toolbar=yes,toolbarposition=top");
return false;
}
};
I can't quite work out the selector for my if statement to change element.tagName to be element.class or similar.
Heres the last thing I tried
document.getElementById("download-link").addEventListener("click", function(e) {
window.open(e.href, "_blank", "location=yes,toolbar=yes,toolbarposition=top");
return false;
e.preventDefault();
});
You mention
should just intercept links with the class 'download-link'
though use .getElementById(). You can use .querySelectorAll() with selector ".download-link" and NodeList.prototype.forEach() to perform a task, see forEach method of Node.childNodes?. For example, attach an event listener, to each ".download-link" element
document.querySelectorAll(".download-link")
.forEach(function(element) {
element.addEventListener("click", function(event) {
// do stuff
})
})
If NodeList.prototype.forEach() is not defined at browser you can use for loop to achieve same result
for (var i = 0, nodes = document.querySelectorAll(".download-link");
nodes && i < nodes.length; i++) {
nodes[i].addEventListener("click", function(event) {
// do stuff
})
}

How to get href of anchor when the event.target is HTMLImageElement?

I want to get the href of an anchor element when it is clicked.
I am using the following javascript code:
document.addEventListener('click', function (event)
{
event = event || window.event;
var el = event.target || event.srcElement;
if (el instanceof HTMLAnchorElement)
{
console.log(el.getAttribute('href'));
}
}, true);
This works perfectly for an embedded anchor such as this:
<div><p><a href='link'></a></p><div>
But it doesn't work when I am working with an anchor and an image:
<div><a href='link'><img></a></div>
The event.target is returning the image instead of the anchor.
The javascript code can be amended with the following if case to get around this:
document.addEventListener('click', function (event)
{
event = event || window.event;
var el = event.target || event.srcElement;
if (el instanceof HTMLImageElement)
{
// Using parentNode to get the image element parent - the anchor element.
console.log(el.parentNode.getAttribute('href'));
}
else if (el instanceof HTMLAnchorElement)
{
console.log(el.getAttribute('href'));
}
}, true);
But this doesn't seem very elegant and I'm wondering if there is a better way.
!IMPORTANT!
NOTE: Keep in mind, I have no access to an ID or class, or any other traditional identifier for that matter. All I know is that there will be an anchor clicked and I need to get its href. I don't even know where it will be, if it exists or will be created later.
EDIT: Please no jQuery or other javascript libraries.
Instead of looping all anchors in the DOM, lookup from the event.target element.
Using JavaScript's .closest() MDN Docs
addEventListener('click', function (event) {
event.preventDefault(); // Don't navigate!
const anchor = event.target.closest("a"); // Find closest Anchor (or self)
if (!anchor) return; // Not found. Exit here.
console.log( anchor.getAttribute('href')); // Log to test
});
<a href="http://stackoverflow.com/a/29223576/383904">
<span>
<img src="//placehold.it/200x60?text=Click+me">
</span>
</a>
<a href="http://stackoverflow.com/a/29223576/383904">
Or click me
</a>
it basically works like jQuery's .closest() which does
Closest or Self (Find closest parent... else - target me!)
better depicted in the example above.
Rather than adding a global click handler, why not just target only anchor tags?
var anchors = document.getElementsByTagName("a");
for (var i = 0, length = anchors.length; i < length; i++) {
var anchor = anchors[i];
anchor.addEventListener('click', function() {
// `this` refers to the anchor tag that's been clicked
console.log(this.getAttribute('href'));
}, true);
};
If you want to stick with the document-wide click handler then you could crawl upwards to determine if the thing clicked is-or-is-contained-within a link like so:
document.addEventListener('click', function(event) {
event = event || window.event;
var target = event.target || event.srcElement;
while (target) {
if (target instanceof HTMLAnchorElement) {
console.log(target.getAttribute('href'));
break;
}
target = target.parentNode;
}
}, true);
This way at least you'd avoid writing brittle code that has to account for all of the possible types of anchor-children and nested structure.

How to get the onclick calling object?

I need to have a handler on the calling object of onclick event.
link
<script>
function click123(event) {
//i need <a> so i can manipulate it with Jquery
}
</script>
I want to do this without the use of $().click or $().live of jQuery but with the method described above.
pass in this in the inline click handler
link
or use event.target in the function (according to the W3C DOM Level 2 Event model)
function click123(event)
{
var a = event.target;
}
But of course, IE is different, so the vanilla JavaScript way of handling this is
function doSomething(e) {
var targ;
if (!e) var e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
}
or less verbose
function doSomething(e) {
e = e || window.event;
var targ = e.target || e.srcElement || e;
if (targ.nodeType == 3) targ = targ.parentNode; // defeat Safari bug
}
where e is the event object that is passed to the function in browsers other than IE.
If you're using jQuery though, I would strongly encourage unobtrusive JavaScript and use jQuery to bind event handlers to elements.
I think the best way is to use currentTarget property instead of target property.
The currentTarget read-only property of the Event interface identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to Event.target, which identifies the element on which the event occurred.
For example:
<span class="icon"></span> blah blah
Javascript:
a.addEventListener('click', e => {
e.currentTarget; // always returns "a" element
e.target; // may return "a" or "span"
})
The easiest way is to pass this to the click123 function or
you can also do something like this(cross-browser):
function click123(e){
e = e || window.event;
var src = e.target || e.srcElement;
//src element is the eventsource
}
http://docs.jquery.com/Events/jQuery.Event
Try with event.target
Contains the DOM element that issued
the event. This can be the element
that registered for the event or a
child of it.
The thing with your method is that you clutter your HTML with javascript. If you put your javascript in an external file you can access your HTML unobtrusive and this is much neater.
Lateron you can expand your code with addEventListener/attackEvent(IE) to prevent memory leaks.
This is without jQuery
link
window.onload = function () {
var el = document.getElementById('elementid');
el.onclick = function (e) {
var ev = e || window.event;
// here u can use this or el as the HTML node
}
}
You say you want to manipulate it with jQuery. So you can use jQuery. Than it is even better to do it like this:
// this is the window.onload startup of your JS as in my previous example. The difference is
// that you can add multiple onload functions
$(function () {
$('a#elementid').bind('click', function (e) {
// "this" points to the <a> element
// "e" points to the event object
});
});

Categories

Resources