How to correctly handle event with dynamically added elements? - javascript

I am developing a ToDo app with HTML and JS, while got stuck with removing dynamically added elements. Is there any way to remove the selected element?
I have tried to delete an element via selecting its parent and then reaching to the child and removing it. Could not arrange to remove the selected element though. el.removeChild(el.childNodes[0]) removes the element from the beginning. Is there any way to remove the selected-active element?
document.addEventListener('click',function(e){
if(e.target && e.target.id== 'rem'){
let el = document.getElementById('uList');
el.removeChild(el.childNodes[0]); // How to remove the clicked el?
}
});
I expect the code to remove the clicked element, rather deleting the nodes starting from the 0 element.
Many thanks!

document.addEventListener('click', function(e) {
if (!e.target || e.target.id !== 'rem') {
return;
}
let parent = e.target.parentElement;
if (!!parent) {
parent.removeChild(e.target);
}
});
for the parent of parent:
document.addEventListener('click', function(e) {
if (!e.target || e.target.id !== 'rem') {
return;
}
const parent = e.target.parentElement;
if (!parent) {
return;
}
const parentOfParent = parent.parentElement;
parent.removeChild(e.target);
if (!!parentOfParent) {
parentOfParent.removeChild(parent);
}
});

Related

Javascript - Problem with modal close on click outside [duplicate]

I have searched for a good solution everywhere, yet I can't find one which does not use jQuery.
Is there a cross-browser, normal way (without weird hacks or easy to break code), to detect a click outside of an element (which may or may not have children)?
Add an event listener to document and use Node.contains() to find whether the target of the event (which is the inner-most clicked element) is inside your specified element. It works even in IE5
const specifiedElement = document.getElementById('a')
// I'm using "click" but it works with any event
document.addEventListener('click', event => {
const isClickInside = specifiedElement.contains(event.target)
if (!isClickInside) {
// The click was OUTSIDE the specifiedElement, do something
}
})
var specifiedElement = document.getElementById('a');
//I'm using "click" but it works with any event
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (isClickInside) {
alert('You clicked inside A')
} else {
alert('You clicked outside A')
}
});
div {
margin: auto;
padding: 1em;
max-width: 6em;
background: rgba(0, 0, 0, .2);
text-align: center;
}
Is the click inside A or outside?
<div id="a">A
<div id="b">B
<div id="c">C</div>
</div>
</div>
You need to handle the click event on document level. In the event object, you have a target property, the inner-most DOM element that was clicked. With this you check itself and walk up its parents until the document element, if one of them is your watched element.
See the example on jsFiddle
document.addEventListener("click", function (e) {
var level = 0;
for (var element = e.target; element; element = element.parentNode) {
if (element.id === 'x') {
document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked";
return;
}
level++;
}
document.getElementById("out").innerHTML = "not x clicked";
});
As always, this isn't cross-bad-browser compatible because of addEventListener/attachEvent, but it works like this.
A child is clicked, when not event.target, but one of it's parents is the watched element (i'm simply counting level for this). You may also have a boolean var, if the element is found or not, to not return the handler from inside the for clause. My example is limiting to that the handler only finishes, when nothing matches.
Adding cross-browser compatability, I'm usually doing it like this:
var addEvent = function (element, eventName, fn, useCapture) {
if (element.addEventListener) {
element.addEventListener(eventName, fn, useCapture);
}
else if (element.attachEvent) {
element.attachEvent(eventName, function (e) {
fn.apply(element, arguments);
}, useCapture);
}
};
This is cross-browser compatible code for attaching an event listener/handler, inclusive rewriting this in IE, to be the element, as like jQuery does for its event handlers. There are plenty of arguments to have some bits of jQuery in mind ;)
How about this:
jsBin demo
document.onclick = function(event){
var hasParent = false;
for(var node = event.target; node != document.body; node = node.parentNode)
{
if(node.id == 'div1'){
hasParent = true;
break;
}
}
if(hasParent)
alert('inside');
else
alert('outside');
}
you can use composePath() to check if the click happened outside or inside of a target div that may or may not have children:
const targetDiv = document.querySelector('#targetDiv')
document.addEventListener('click', (e) => {
const isClickedInsideDiv = e.composedPath().includes(targetDiv)
if (isClickedInsideDiv) {
console.log('clicked inside of div')
} else {
console.log('clicked outside of div')
}
})
I did a lot of research on it to find a better method. JavaScript method .contains go recursively in DOM to check whether it contains target or not. I used it in one of react project but when react DOM changes on set state, .contains method does not work. SO i came up with this solution
//Basic Html snippet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="mydiv">
<h2>
click outside this div to test
</h2>
Check click outside
</div>
</body>
</html>
//Implementation in Vanilla javaScript
const node = document.getElementById('mydiv')
//minor css to make div more obvious
node.style.width = '300px'
node.style.height = '100px'
node.style.background = 'red'
let isCursorInside = false
//Attach mouseover event listener and update in variable
node.addEventListener('mouseover', function() {
isCursorInside = true
console.log('cursor inside')
})
/Attach mouseout event listener and update in variable
node.addEventListener('mouseout', function() {
isCursorInside = false
console.log('cursor outside')
})
document.addEventListener('click', function() {
//And if isCursorInside = false it means cursor is outside
if(!isCursorInside) {
alert('Outside div click detected')
}
})
WORKING DEMO jsfiddle
using the js Element.closest() method:
let popup = document.querySelector('.parent-element')
popup.addEventListener('click', (e) => {
if (!e.target.closest('.child-element')) {
// clicked outside
}
});
To hide element by click outside of it I usually apply such simple code:
var bodyTag = document.getElementsByTagName('body');
var element = document.getElementById('element');
function clickedOrNot(e) {
if (e.target !== element) {
// action in the case of click outside
bodyTag[0].removeEventListener('click', clickedOrNot, true);
}
}
bodyTag[0].addEventListener('click', clickedOrNot, true);
Another very simple and quick approach to this problem is to map the array of path into the event object returned by the listener. If the id or class name of your element matches one of those in the array, the click is inside your element.
(This solution can be useful if you don't want to get the element directly (e.g: document.getElementById('...'), for example in a reactjs/nextjs app, in ssr..).
Here is an example:
document.addEventListener('click', e => {
let clickedOutside = true;
e.path.forEach(item => {
if (!clickedOutside)
return;
if (item.className === 'your-element-class')
clickedOutside = false;
});
if (clickedOutside)
// Make an action if it's clicked outside..
});
I hope this answer will help you !
(Let me know if my solution is not a good solution or if you see something to improve.)

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>

Select the li element only once on mousemove

I have a list of li elements.
I'm trying to select these elements upon mouseclick + mousemove.
However, it is selecting the same elements several times inside my mousemove function:
this.querySelector('.directory-list').addEventListener('mousedown', (e) => {
this.querySelector('.directory-list').onmousemove = function(e) {
this._target = (e.target) ? e.target: e.srcElement;
this._target.select();
}
});
Let's say I have something like this:
If I want to select these two directories with my mouse, it would select them about 10 times because of my "onmousemove" function.
Is there any way to get rid of duplicates while doing that ?
You could check to see if __target is defined and then only define/select it if not.
this.querySelector('.directory-list').addEventListener('mousedown', (e) => {
this.querySelector('.directory-list').onmousemove = function(e) {
if (!this_target) {
this._target = (e.target) ? e.target: e.srcElement;
this._target.select();
}
}
});
then set it to null on mouseup.

Javascript Detect click event outside of div [duplicate]

This question already has answers here:
How do I detect a click outside an element?
(91 answers)
Closed 1 year ago.
The community reviewed whether to reopen this question last year and left it closed:
Original close reason(s) were not resolved
I have a div with id="content-area", when a user clicks outside of this div, I would like to alert them to the fact that they clicked outside of it. How would I use JavaScript to solve this issue?
<div id = "outer-container">
<div id = "content-area">
Display Conents
</div>
</div>
In pure Javascript
Check out this fiddle and see if that's what you're after!
document.getElementById('outer-container').onclick = function(e) {
if(e.target != document.getElementById('content-area')) {
document.getElementById('content-area').innerHTML = 'You clicked outside.';
} else {
document.getElementById('content-area').innerHTML = 'Display Contents';
}
}
http://jsfiddle.net/DUhP6/2/
The Node.contains() method returns a Boolean value indicating whether a node is a descendant of a given node or not
You can catch events using
document.addEventListener("click", clickOutside, false);
function clickOutside(e) {
const inside = document.getElementById('content-area').contains(e.target);
}
Remember to remove the event listened in the right place
document.removeEventListener("click", clickOutside, false)
Bind the onClick-Event to an element that is outside your content area, e.g. the body. Then, inside the event, check whether the target is the content area or a direct or indirect child of the content area. If not, then alert.
I made a function that checks whether it's a child or not. It returns true if the parent of a node is the searched parent. If not, then it checks whether it actually has a parent. If not, then it returns false. If it has a parent, but it's not the searched one, that it checks whether the parent's parent is the searched parent.
function isChildOf(child, parent) {
if (child.parentNode === parent) {
return true;
} else if (child.parentNode === null) {
return false;
} else {
return isChildOf(child.parentNode, parent);
}
}
Also check out the Live Example (content-area = gray)!
I made a simple and small js library to do this for you:
It hijacks the native addEventListener, to create a outclick event and also has a setter on the prototype for .onoutclick
Basic Usage
Using outclick you can register event listeners on DOM elements to detect whether another element that was that element or another element inside it was clicked. The most common use of this is in menus.
var menu = document.getElementById('menu')
menu.onoutclick = function () {
hide(menu)
}
this can also be done using the addEventListener method
var menu = document.getElementById('menu')
menu.addEventListener('outclick', function (e) {
hide(menu)
})
Alternatively, you can also use the html attribute outclick to trigger an event. This does not handle dynamic HTML, and we have no plans to add that, yet
<div outclick="someFunc()"></div>
Have fun!
Use document.activeElement to see which of your html elements is active.
Here is a reference:
document.activeElement in MDN
$('#outer-container').on('click', function (e) {
if (e.target === this) {
alert('clicked outside');
}
});
This is for the case that you click inside the outer-container but outside of the content-area.
Here is the fiddle : http://jsfiddle.net/uQAMm/1/
$('#outercontainer:not(#contentarea)').on('click', function(event){df(event)} );
function df(evenement)
{
var xstart = $('#contentarea').offset().left;
var xend = $('#contentarea').offset().left + $('#contentarea').width();
var ystart = $('#contentarea').offset().top;
var yend = $('#contentarea').offset().top + $('#contentarea').height();
var xx = evenement.clientX;
var yy = evenement.clientY;
if ( !( ( xx >= xstart && xx <= xend ) && ( yy >= ystart && yy <= yend )) )
{
alert('out');
}
}
use jquery as its best for DOM access
$(document).click(function(e){
if($(e.target).is("#content-area") || $(e.target).closest("#content-area").length)
alert("inside content area");
else alert("you clicked out side content area");
});
Put this into your document:
<script>
document.onclick = function(e) {
if(e.target.id != 'content-area') alert('you clicked outside of content area');
}
</script>
Here is a simple eventListener that checks all parent elements if any contain the id of the element. Otherwise, the click was outside the element
html
<div id="element-id"></div>
js
const handleMouseDown = (ev) => {
let clickOutside = true
let el = ev.target
while (el.parentElement) {
if (el.id === "element-id") clickOutside = false
el = el.parentElement
}
if (clickOutside) {
// do whatever you wanna do if clicking outside
}
}
document.addEventListener("mousedown", handleMouseDown)

jQuery trigger event when click outside the element

$(document).click(function(evt) {
var target = evt.currentTarget;
var inside = $(".menuWraper");
if (target != inside) {
alert("bleep");
}
});
I am trying to figure out how to make it so that if a user clicks outside of a certain div (menuWraper), it triggers an event.. I realized I can just make every click fire an event, then check if the clicked currentTarget is same as the object selected from $(".menuWraper"). However, this doesn't work, currentTarget is HTML object(?) and $(".menuWraper") is Object object? I am very confused.
Just have your menuWraper element call event.stopPropagation() so that its click event doesn't bubble up to the document.
Try it out: http://jsfiddle.net/Py7Mu/
$(document).click(function() {
alert('clicked outside');
});
$(".menuWraper").click(function(event) {
alert('clicked inside');
event.stopPropagation();
});
http://api.jquery.com/event.stopPropagation/
Alternatively, you could return false; instead of using event.stopPropagation();
if you have child elements like dropdown menus
$('html').click(function(e) {
//if clicked element is not your element and parents aren't your div
if (e.target.id != 'your-div-id' && $(e.target).parents('#your-div-id').length == 0) {
//do stuff
}
});
The most common application here is closing on clicking the document but not when it came from within that element, for this you want to stop the bubbling, like this:
$(".menuWrapper").click(function(e) {
e.stopPropagation(); //stops click event from reaching document
});
$(document).click(function() {
$(".menuWrapper").hide(); //click came from somewhere else
});
All were doing here is preventing the click from bubbling up (via event.stopPrpagation()) when it came from within a .menuWrapper element. If this didn't happen, the click came from somewhere else, and will by default make it's way up to document, if it gets there, we hide those .menuWrapper elements.
try these..
$(document).click(function(evt) {
var target = evt.target.className;
var inside = $(".menuWraper");
//alert($(target).html());
if ($.trim(target) != '') {
if ($("." + target) != inside) {
alert("bleep");
}
}
});
$(document).click((e) => {
if ($.contains($(".the-one-you-can-click-and-should-still-open").get(0), e.target)) {
} else {
this.onClose();
}
});
I know that the question has been answered, but I hope my solution helps other people.
stopPropagation caused problems in my case, because I needed the click event for something else. Moreover, not every element should cause the div to be closed when clicked.
My solution:
$(document).click(function(e) {
if (($(e.target).closest("#mydiv").attr("id") != "mydiv") &&
$(e.target).closest("#div-exception").attr("id") != "div-exception") {
alert("Clicked outside!");
}
});
http://jsfiddle.net/NLDu3/
I do not think document fires the click event. Try using the body element to capture the click event. Might need to check on that...
This code will open the menu in question, and will setup a click listener event. When triggered it will loop through the target id's parents until it finds the menu id. If it doesn't, it will hide the menu because the user has clicked outside the menu. I've tested it and it works.
function tog_alerts(){
if($('#Element').css('display') == 'none'){
$('#Element').show();
setTimeout(function () {
document.body.addEventListener('click', Close_Alerts, false);
}, 500);
}
}
function Close_Alerts(e){
var current = e.target;
var check = 0;
while (current.parentNode){
current = current.parentNode
if(current.id == 'Element'){
check = 1;
}
}
if(check == 0){
document.body.removeEventListener('click', Close_Alerts, false);
$('#Element').hide();
}
}
function handler(event) {
var target = $(event.target);
if (!target.is("div.menuWraper")) {
alert("outside");
}
}
$("#myPage").click(handler);
try this one
$(document).click(function(event) {
if(event.target.id === 'xxx' )
return false;
else {
// do some this here
}
});
var visibleNotification = false;
function open_notification() {
if (visibleNotification == false) {
$('.notification-panel').css('visibility', 'visible');
visibleNotification = true;
} else {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
$(document).click(function (evt) {
var target = evt.target.className;
if(target!="fa fa-bell-o bell-notification")
{
var inside = $(".fa fa-bell-o bell-notification");
if ($.trim(target) != '') {
if ($("." + target) != inside) {
if (visibleNotification == true) {
$('.notification-panel').css('visibility', 'hidden');
visibleNotification = false;
}
}
}
}
});

Categories

Resources