I am thinking of to add a javascript function to capture all the <a> click events inside a html page.
So I am adding a global function that governs all the <a> click events, but not adding onclick to each (neither using .onclick= nor attachEvent(onclick...) nor inline onclick=). I will leave each <a> as simple as <a href="someurl"> within the html without touching them.
I tried window.onclick = function (e) {...}
but that just captures all the clicks
How do I specify only the clicks on <a> and to extract the links inside <a> that is being clicked?
Restriction: I don't want to use any exra libraries like jQuery, just vanilla javascript.
Use event delegation:
document.addEventListener(`click`, e => {
const origin = e.target.closest(`a`);
if (origin) {
console.clear();
console.log(`You clicked ${origin.href}`);
}
});
<div>
some link
<div><div><i>some other (nested) link</i></div></div>
</div>
[edit 2020/08/20] Modernized
You can handle all click using window.onclick and then filter using event.target
Example as you asked:
<html>
<head>
<script type="text/javascript">
window.onclick = function(e) { alert(e.target);};
</script>
</head>
<body>
google
yahoo
facebook
</body>
</html>
window.onclick = function (e) {
if (e.target.localName == 'a') {
console.log('a tag clicked!');
}
}
The working demo.
your idea to delegate the event to the window and then check if the "event.target" is a link, is one way to go (better would be document.body). The trouble here is that it won't work if you click on a child node of your element. Think:
<b>I am bold</b>
the target would be the <b> element, not the link. This means checking for e.target won't work. So, you would have to crawl up all the dom tree to check if the clicked element is a descendant of a <a> element.
Another method that requires less computation on every click, but costs more to initialize would be to get all <a> tags and attach your event in a loop:
var links = Array.prototype.slice.call(
document.getElementsByTagName('a')
);
var count = links.length;
for(var i = 0; i < count; i++) {
links[i].addEventListener('click', function(e) {
//your code here
});
}
(PS: why do I convert the HTMLCollection to array? here's the answer.)
You need to take into account that a link can be nested with other elements and want to traverse the tree back to the 'a' element. This works for me:
window.onclick = function(e) {
var node = e.target;
while (node != undefined && node.localName != 'a') {
node = node.parentNode;
}
if (node != undefined) {
console.log(node.href);
/* Your link handler here */
return false; // stop handling the click
} else {
return true; // handle other clicks
}
}
See e.g. https://jsfiddle.net/hnmdijkema/nn5akf3b/6/
You can also try using this:
var forEach = Array.prototype.forEach;
var links = document.getElementsByTagName('a');
forEach.call(links, function (link) {
link.onclick = function () {
console.log('Clicked');
}
});
It works, I just tested!
Working Demo: http://jsfiddle.net/CR7Sz/
Somewhere in comments you mentioned you want to get the 'href' value you can do that with this:
var forEach = Array.prototype.forEach;
var links = document.getElementsByTagName('a');
forEach.call(links, function (link) {
link.onclick = function () {
console.log(link.href); //use link.href for the value
}
});
Demo: http://jsfiddle.net/CR7Sz/1/
Try jQuery and
$('a').click(function(event) { *your code here* });
In this function you can extract href value in this way:
$(this).attr('href')
Some accepted answers dont work with nested elements like:
<font><u>link</u></font>
There is a basic solution for most cases:
```
var links = document.getElementsByTagName('a');
for(var i in links)
{
links[i].onclick = function(e){
e.preventDefault();
var href = this.href;
// ... do what you need here.
}
}
If anybody is looking for the typed version (TypeScript, using Kooilnc's answer), here it is:
document.addEventListener("click", (e: Event) => {
if(!e.target) { return; }
if(!(e.target instanceof Element)) { return; }
const origin = e.target.closest("a");
if(!origin || !origin.href) { return; }
console.log(`You clicked ${origin.href}`);
});
I guess this simple code will work with jquery.
$("a").click(function(){
alert($(this).attr('href'));
});
Without JQuery:
window.onclick = function(e) {
if(e.target.localName=='a')
alert(e.target);
};
The above will produce the same result.
Very simple :
document.getElementById("YOUR_ID").onclick = function (e) {...}
The selector is what you want to select so lets say you have button called
Button1
The code to capure this is:
document.getElementById("button1").onclick = function (e) { alert('button1 clicked'); }
Hope that helps.
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.)
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>
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 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.
I want to detect in my firefox extension if a link has been clicked. So far, for this I add a click event listener to the window
window.addEventListener("click", function(event) { handleWindowClick(event); }, false);
...
handleWindowClick : function(event) {
if ("event.target is a link") {
// do something
}
};
For some links the event.target is simple the URL. However, for some links I get, e.g., a HTMLSpanElement as event.target. Am I on the right track to catch link clicks or are there other ways? If it works this way, how can I ensure the successfully test if the event.targer is a link?
You're adding an event listener to the main window which registers any click. The url's you're having problems with must be wrapped in a <span> tag. What you need is event delegation
Why don't you just put the click event listener to anchors (<a>)?
var hrefs = document.getElementsByTagName('a');
for (i = 0; i < hrefs.length; i++) {
hrefs[i].addEventListener(...)
...
}
or in jQuery:
$('a').click(function () {
...
});
check this out, i hope this is what you are looking for.
window.addEventListener("click", function(event) {
handleWindowClick(event);
}, false);
function handleWindowClick(event){
var origEl = event.target || event.srcElement;
if(origEl.tagName === 'A')
alert("anchor link is clicked");
else if(origEl.parentNode.tagName === 'A')
alert("clicked inside anchor");
else if(origEl.tagName === 'SPAN')
alert("span is clicked");
}
fiddle : http://jsfiddle.net/5zXkN/3/
I extended the answer of dku.rajkumar to support arbitrary constructs within "A"-tags. I'm simply go up the tree until I either find an "A" or I'm at the root (so no link clicked in this case). It seems to do the trick. Thanks to all for your help!
window.addEventListener("click", function(event) { handleWindowClick(event); }, false);
...
isLink : function(element) {
if(element.tagName === 'A')
return true;
else
if (element.parentNode)
return this.isLink(element.parentNode)
else
return false;
},
handleWindowClick : function(event) {
var element = event.target || event.srcElement;
var isLink = this.isLink(element);
if (isLink)
dump("A link has been clicked.\n");
},