JavaScript delegate for elements with child nodes - javascript

In a simple example, I try to make delegate for first-level children of an element. The problem comes when the child elements have children. The mouse event consider the clicked element (regardless of level).
My solution is to cycle until reaching the first-level child; but I wonder if this is the best method to do so.
Isn't there a method for directly returning the first-level children upon delegate click?
JS
window.onload=function(){
document.getElementById('test').addEventListener('click', function(e){
console.log(e.target);alert(e.target.id);
}, false);
}
HTML
<div id="test">
First
<b>Second</b>
Third
Fourth
<div id="div">Division</div>
<div id="div2"><span>Division</span></div>
</div>

If it's only the first level elements you're after, you could check if the parentNode of the clicked element is the root, no further traversing needed. Otherwise you'll need to traverse up to the first child of the root. Something like:
// level0 is the root
level0.addEventListener('click', function(e) {
var from = e.target || e.srcElement;
from = from.parentNode === level0 ? from : findFirst(level0,from);
/** do things **/
}, false);
// traverse up to first child of [root]
function findFirst(root,el){
while(true){
el = el.parentNode;
if (el && el.parentNode === root){
return el;
}
}
return null;
}
Here is a fork of your jsfiddle, using the above.

using only javascript you can use a recursive function that will find the first parent element with an id:
function findParentWithId(element){
if(element.id){
return element;
}
return findParentWithId(element.parentNode);
}
​and then, use it with the target element of the event
document.getElementById('test').addEventListener('click', function(e){
console.log(findParentWithId(e.target));alert(findParentWithId(e.target).id);
}, false);

I'm not sure what's beeing ask here. Events do Bubble by default (which is actually not true, by default they would traverse top->down) because its most common to call .addEventListener with false as third argument.
You can stop most events from further bubbling up, by calling eventObject.stopPropagation() on the node where you want to stop it.
If you want to "prefilter" only the first-level children, you shouldn't use delegation at all, but query with .querySelectorAll like
[].forEach.call( document.querySelectorAll('#test > *'), function( node ) {
node.addEventListener('click', function( event ) {
alert( this.id );
}, false);
});
That would bind click event listeners on all direct children from #test.
See that in action: http://jsfiddle.net/8Xmn4/

Getting a reference to all first level children of a node is simple:
window.onload=function(){
document.getElementById('test').addEventListener('click', function(e){
return document.getElementById('test').children;
}, false);
}
this outputs, on Firebug format:
[a#first #, a#second #, a#third #, a#fourth #, div#div, div#div]
Obviously, it returns the children with their respective children, but the first level reference remains.

jQuery(':first-child') use this jquery to get the first child

Related

How can i get multiple ids getElementById

first of all sorry for my low english..
i have this code:
<input id="id1" type="text"/>
<input id="id2" type="text"/>
document.getElementById("id1").onkeypress = function(e) {
var chr = String.fromCharCode(e.which);
if ("><abc/\"".indexOf(chr) >= 0)
return false;
};
How can I choose the second id? Example : id1, id2 i want use this code
Thanks!
Using jQuery:
$('#id1, #id2').on('keypress', function(e) {
var chr = String.fromCharCode(e.which);
if ("><abc/\"".indexOf(chr) >= 0)
return false;
});
You can expand the list of id selectors: "#id1, #id2, #id3, ...".
But if there are too many elements, it's better to assign them a class and target them like this:
$('.className').on('keypress', function(e) {
var chr = String.fromCharCode(e.which);
if ("><abc/\"".indexOf(chr) >= 0)
return false;
});
Instead of using id's in the first place, which leads to brittle code (like you are seeing with your problem) and doesn't scale well, set up the event on an ancestor element and handle the event when it bubbles up to that ancestor element. This is called event delegation and simplifies your code. When handling the event, the event.target element will hold a reference to the actual element that triggered the event in the first place, so you don't need to know its id.
// Use modern standards to event handling with .addEventListener(), rather than
// setting up event properties. Here, we're handling the event at the wrapper
// element, so when an event originates from a descendant, it will bubble up
// and be handled here.
document.getElementById("wrapper").addEventListener("keydown", function(e) {
// The event.target references the element where the event was triggered
// To get the character that was presssed, use event.key
// indexOf returns -1 when no match is found.
// Instead of checking for the condition where you should do
// nothing, check for the condition where you should do something
if ("><abc/\"".indexOf(e.key) > -1){
console.log("Match found");
}
});
<div id="wrapper">
<input type="text">
<input type="text">
</div>

JavaScripts equivalent to jQuery document click with a class

What is the JavaScript equivalent to this jQuery:
$(document).on('click', '.add-star', function (event) {
//event will return the .add-star
})
Markup looks like this
<div class="add-star">
<svg>
<path />
</svg>
</div>
When I do document.addEventListener('click', function(e) {... the e.target gets me the path not the parent add-star. From what I know with the jQuery way it bubbles up on the event looking for the class specified and returns that in the event. But there is no class specified with the JS event, so it returns just the immediate clicked element, the path from the svg.
How would I return add-star from the js event?
It's pretty easy. You just use .matches() on each element starting at e.target, traversing through each .parentNode until the bound element. When/if a match is found, you call the function.
So create a function that receives the callback and returns a new function handles this operation.
function delegate(selector, handler) {
return function(event) {
var el = event.target;
do {
if (el.matches(selector)) {
handler.call(el, event);
}
} while ((el = el.parentNode) && el !== this);
};
}
Then call that function to create the handler.
document.addEventListener('click', delegate('.add-star', function (event) {
//event will return the .add-star
}));
You have two main ways of handling events here, the event delegation method is similar to what your jQuery example is doing so I'll make that #1. This method uses e.target.matches to accomplish checking for an element that might not exist. The second method is for more traditional elements and uses document.querySelector
Method 1 delegated events
document.addEventListener('click', e => {
if (!e.target.matches('.add-star')) { return }
// do stuff
});
Method 2 non-dynamic selectors
let ele = document.querySelector('.add-star');
ele.addEventListener('click', e => { // do stuff });

Javascript - trigger custom parent html event from child element

I am trying to trigger a custom event in a parent element from the child elements event. The parent element is HelpMenuHeader and it's custom event is defined in HTML as "onsubmenu_click".
Here's a snippet of the HTML that just shows one menu tree.
<span class="formMenu" id="HelpMenuHeader" onsubmenu_click="OnMenuClick()">Help
<div class="formMenu" id="HelpAbout" onmouseup="MenuChildClick()">About us...</div>
</span>
In the child element, HelpAbout, the MenuChildClick event needs to trigger the parent's onsubmenu_click event so that that will execute (that event handler uses the parent's information).
Here's a snippet of the javascript I have for MenuChildClick:
function MenuChildClick()
{
var srcElement = this.event.srcElement;
if (srcElement.id != "spacer" && srcElement.tagName != "HR")
{
// NONE OF THE LINES BELOW WORK
//parent.$(srcElement).trigger('onsubmenu_click');
//$(srcElement).trigger('onsubmenu_click');
//var event = document.createEvent('Event');
//event.initEvent('submenu_click', true, true, null);
//srcElement.dispatchEvent(event);
//oEvent = createEventObject();
//oEvent.result = srcElement.id;
//onsubmenu_click.fire(oEvent);
}
}
I'm having a problem getting a reference to the correct parent element in the MenuChildClick event because when I check the parent reference doesn't have the parent ID.
And then once I have the correct parent reference I need to execute the parent's onsubmenu_click custom event. (The parent event is already being listened to since it's defined in the HTML, right?)
I have to support IE compatibility view so I need it to work for previous IE versions as well.
Anyone tell me how I can do these things (1 & 2 above) leaving the HTML as it is?
Thanks in advance.
You can use jQuery methods .on() and .trigger() instead of event handler attribute
$(function() {
function parentHandler(event, args) {
console.log(event.type, args)
}
$("#HelpMenuHeader").on("submenu_click", parentHandler);
$("#HelpAbout").on("mouseup", function() {
$(this).parent().trigger("submenu_click"
, ["triggered from #" + this.id])
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
<span class="formMenu" id="HelpMenuHeader">Help
<div class="formMenu" id="HelpAbout">About us...</div>
</span>
First you have to pass the element that is triggering the event in your HTML by changing your HTML to this:
<span class="formMenu" id="HelpMenuHeader" onsubmenu_click="OnMenuClick()">Help
<div class="formMenu" id="HelpAbout" onmouseup="MenuChildClick(this); return false;">About us...</div>
</span>
Notice that I pass the element that is triggering the function call by passing 'this' through the onmouseup function call.
Then you can use the passed element to define which elements you want to monitor as follows:
function MenuChildClick(element)
{
var srcElement = element;
var parent = element.parentNode
if (srcElement.id != "spacer" && srcElement.tagName != "HR")
{
//parent.trigger('onsubmenu_click');
}
}

JS event listener for a type of element?

Is there a way to add some kind of listener for a type of html element? For example if i wanna call a function when the user clicks any p element
the easiest answer would be using addEventListener() if you want a specific html tag just like what i wanted in my question then you'll find the answer there ill paraphrase it here too
add this
<script>
document.addEventListener("click", function(e){
//your desired nodeName like : DIV , SPAN , LI etc
if(e.target && e.target.nodeName== 'DIV')
//add a function below to trigger
{alert('bingo')}
});
</script>
to the end of your document
by the way don't forget to use uppercase nodeNames or just put a toLowerCase() before it. cheers :)
Add the event listener to the window / document / document.body and check the type of the element and the types of its parents because if you have a <span> inside a <p>, clicking the span won't trigger the click in the paragraph.
document.addEventListener("click", function (eventArgs) {
var target = eventArgs.target;
var elementToLookFor = "p";
while (target !== null) {
if (target.tagName.toLowerCase() === elementToLookFor) {
// Do magic stuff with the paragraph
console.log(target);
}
target = target.parentElement;
}
});
This technique is called "event delegation."
Edit: Note that you cannot early return from the loop above. If your have nested paragraphs, i.e.
<p>
Hey,
<p>there!</p>
</p>
Having an early return will only call your event handler for the inner paragraph, whereas you said that you'd like the handler to be invoked on every paragraph which is why you need to traverse all the ancestors of the element.
I assume that you are looking for code along these lines:
var paras = document.getElementsByTagName("p");
// Loop through elements.
for(var i = 0; i < paras.length; i++) {
// Add listener.
paras[i].addEventListener("click",
function() {
// Execute function.
}, false);
}
I'd just select all the elements on the page and add eventListeners on them like so:
function addListeners(elementType, eventType, callback) {
Array.prototype.slice.call(document.querySelectorAll(elementType)).forEach(function (el, i) {
el.addEventListener(eventType, callback, false);
});
}
Above we use querySelectorAll to pick all the wanted elements, convert it to an Array (if you use es6, you can use Array.from) and then we loop through the array and add listeners with the wanted callback.
Here's an example: https://jsfiddle.net/a7en4d4s/
Look at this JSFiddle, and see if it works for you
<span>Click Yes</span><br/><br/>
<span>Click No</span><br/><br/>
<a>Clicked: <b id="result"></b></a>
<script>
$("span").click(function(){
var a = $(this).html();
$("#result").html(a);
});
</script>

Find parent element in JQuery event

I've added a click event as follows and would like to check if the target has a specific parent.
$(document).click(function(event){
// Check here if target has specific parent for example -> #parent
});
How can this be done?
There's a .parent() dom traversal method for this.
according to Pointy's crystal ball, you probably want to do something like this:
$(document).click(function(event) {
if ($(event.target).parents('.selector').length > 0) {
}
});
I'm not sure why are you set click handler on document, maybe looking for event delegation and the .on()?
I believe this also works.. AFAIK jQuery events use the the literal element instead of a jQuery object when calling events. Basically this should be your normal DOM element with normal JavaScript properties.
$(document).click(function(event)
{
let myparent = $(this.parentNode); //jquery obj
let parent = $(this.parentNode)[0]; //plain DOM obj
let myself = $(this); //jquery obj;
let $elf = this; //plain DOM obj
});
Note: sometimes using 'self' as a variable is bad/causes conflicts with certain libraries so i used $elf. The $ in $elf is not special; not a jQuery convention or anything like that.
$(document).click(function(event){
var $parent = $(this).parent();
// test parent examples
if($parent.hasClass('someclass')) { // do something }
if($parent.prop('id') == 'someid')) { // do something }
// or checking if this is a decendant of any parent
var $closest = $(this).closest('someclass');
if($closest.length > 0 ) { // do something }
$closest = $(this).closest('#someid');
if($closest.length > 0 ) { // do something }
});
I have reliably used this in the past:
var target = $( event.target )
This will give you a reference to the jQuery object for the element that had the event invoked. You could use this same approach and see if the parent is "#parent", something like this:
var target = $( event.target )
if (target.parent().attr('id') == "#parent") {
//do something
}

Categories