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.
Related
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.)
What I'm trying to accomplish:
When a user tabs over and hits enter on any labels present in my application (the months or percentage labels), it simulates a click on whichever label you're on.
Here's the JS code:
///////// Accessibility
// Grab label ID's for labels
const allLabels = document.querySelectorAll("labels");
// On hitting enter of any month or percentage label, trigger a click
allLabels.addEventListener("keyup", onEnter);
function onEnter(e) {
e.preventDefault();
if (e.keyCode === 13) {
allLabels.click();
}
}
It currently throws an error of, "allLabels.addEventListener is not a function", and also of course doesn't work.
Any idea? Thanks in advance.
document.querySelectorAll returns NodeList not an element.
So, you need to loop over all Element and add Event
const allLabels = document.querySelectorAll("label");
allLabels.forEach(label => label.addEventListener("keyup", onEnter));
function onEnter(e) {
e.preventDefault();
let currentLabel = e.target; // e.target returns the label on which user press Enter
if (e.keyCode === 13) {
//allLabels.forEach(label=>label.click());
currentLabel.click();
}
}
OR
You can add Event to the Parent Element which contains all the label.
document.addEventListener("keyup",onEnter,true);
function onEnter(event) {
let element = event.target;
if(element.nodeName==="LABEL" && event.keyCode === 13) {
allLabels.forEach(label=>label.click());
}
}
The method querySelectorAll returns an array of nodes that match with the selector used. For that reason, you cannot call the addEventListener method over an array.
You need to iterate the array and bind the listener for each element:
const allLabels = document.querySelectorAll("labels");
Array.from(allLabels).forEach(label => label.addEventListener('keyup', onEnter))
Try this.
const allLabels = document.querySelectorAll("labels);
allLabels.forEach( label => {
label.addEventListener('keyup', onEnter);
});
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);
}
});
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
})
}
I would like to catch some events for a specific div if the user clicked on the div (focus the div), keyboard events are catch (not if the last click was out of the div (unfocus the div)
I tried some things, but haven't succeeded : JSFiddle
document.getElementById("box").onkeydown = function(event) {
if (event.keyCode == 13) { // ENTER
alert("Key ENTER pressed");
}
}
This code doesn't work even if I click on the div.
Pure JS solution please
The div element isn't interactive content by default. This means that there isn't a case where the return key will ever trigger on it. If you want your div element to be interactive you can give it the contenteditable attribute:
<div id="box" contenteditable></div>
In order to now fire the event you need to first focus the div element (by clicking or tabbing into it). Now any key you press will be handled by your onkeydown event.
JSFiddle demo.
Giving the 'div' a tabindex should do the trick, so the div can have the focus:
<div id="box" tabindex="-1"></div>
If you click on the div it gets the focus and you can catch the event.
JSFIDDEL
If you set 'tabindex' > 0 you can also select the div using TAB.
You could catch all the click events, then check if the event target was inside the div:
var focus_on_div = false;
document.onclick = function(event) {
if(event.target.getAttribute('id') == 'mydiv') {
focus_on_div = true;
} else {
focus_on_div = false;
}
}
document.onkeyup = function(event) {
if (focus_on_div) {
// do stuff
}
}
try this code i hope this work
var mousePosition = {x:0, y:0};
document.addEventListener('mousemove', function(mouseMoveEvent){
mousePosition.x = mouseMoveEvent.pageX;
mousePosition.y = mouseMoveEvent.pageY;
}, false);
window.onkeydown = function(event) {
var x = mousePosition.x;
var y = mousePosition.y;
var elementMouseIsOver = document.elementFromPoint(x, y);
if(elementMouseIsOver.id == "box" && event.keyCode == "13") {
alert("You Hate Enter Dont You?");
}
}
DEMO