I currently have a list of <li>'s. Each <li> will have a color class defined, example: .color-blue, .color-red, .color-green - like so:
<ul id="listings">
<li class="layer block color-blue" id="item-1"></li>
<li class="layer block color-red" id="item-2"></li>
<li class="layer block color-green" id="item-3"></li>
</ul>
How do I copy/get the color class of the specific <li> item that is clicked?
I have my click listener in place and also know how to get the <li "id"> however not sure on the specific class though.
/* Click listener */
document.getElementById("listings").addEventListener("click", function(e) {
//console.log(e.target.id + " was clicked");
});
Something like this:
document.getElementById("listings").addEventListener("click", function(e) {
var el = e.target;
if (el.tagName == "LI") { // Use only li tags
for (i=0; i < el.classList.length; i++) {
if (~el.classList[i].indexOf('color')) {
var color = el.classList[i];
console.log('color class found: '+color);
break;
}
}
}
});
http://jsfiddle.net/bHJ3n/
You can use (jQuery):
$('ul').find('li.layer block color-blue')
Or
$('ul#listings').find('li.layer block color-blue')
Or... you can not use jQuery as that wasn't in the original question and would be wasteful to include unnecessarily.
Here's a solution that works in vanilla JS:
jsFiddle Example
Essentially because you're lumping the colour among the other classes you have to split them into an array and iterate over them until you find the one that starts 'color-'. I would recommend you use a custom attribute instead, like data-color="blue" as that would mean you could just retrieve it with:
e.target.getAttribute('data-color');
Try
document.getElementById("listings").addEventListener("click", function(e) {
alert(e.srcElement.className);
});
DEMO
UPDATE(since it is not working in Firefox as pointed from Sai):
To work also in Firefox try this:
document.getElementById("listings").addEventListener("click", function(e) {
var target = e.target || e.srcElement;
alert(target.className);
});
DEMO2
Related
I have the following structure:
<ul id="list-items">
<li class="item" data-id="123" data-title="Some Title">
<div class="block">
<img src="#"/>
Link
<p>Some excerpt</p>
</div>
</li>
</ul>
There are more than 1 <li> item
All data- attributes are on the <li> elements
Using jQuery, I would usually make use of event delegation instead of attaching event handlers on every <li> item:
$( document ).on( 'click', '.item', function() {
var id = $( this ).data( 'id' );
var title = $( this ).data( 'title' );
});
However, I am not able to replicate this using Pure JavaScript.
I want to be able to click on an <li> item without clicking any of its child elements.
I am also not at the liberty of using closest() since we have to provide support for IE11. Is there a simpler way to implement it?
EDIT:
I am avoiding attaching event listeners to each <li> item as it won't work for dynamically created <li> elements, and also for performance reasons.
You can disable the li's content from getting any mouse event by setting a pointer-events: none to it.
<li class="item" data-id="123" data-title="Some Title">
<div class="block" style="pointer-events: none">
<img src="#"/>
Link
<p>Some excerpt</p>
</div>
</li>
You can guarantee now that the event.target will always be the li
If you don't want to attach event handler on all the items directly. you can attach only one event handler on the parent like this
var element = document.querySelector("#vanilla-parent")
element.addEventListener("click", function(event){
event.composedPath().forEach(function(elm){
if (elm.tagName === 'LI') {
// do something
}
});
});
$("#jquery-parent").on("click", "li", function(event){
// do something
});
Pen demonstrating the same: https://codepen.io/kireeti-tiki/pen/EzPpqZ?editors=1010
I used composedPath on the event object to get to li, I wouldn't recommend this as this is a bit of hacky way to get to the solution. Also, it is not supported on IE and Edge. So stay away from that solution if you need support for those browsers. More on that subject here https://developer.mozilla.org/en-US/docs/Web/API/Event
If using jQuery is not a problem, Then I would recommend that approach.
Will something like get all li and attach an eventlistener do?
<script>
var li_list = document.getElementsByTagName('li');
for(var i = 0; i < li_list.length; i++) {
(function(index) {
li_list[index].addEventListener("click", function() {
event.preventDefault();
alert('clicked')
})
})(i);
}
</script>
Here's an example using Element.closest(), for which there is a polyfill.
function attachClickHandler() {
const list = document.querySelector('.list');
list.addEventListener('click', (item) => {
// console.log('Item:', item);
const closest = item.target.closest('li');
console.log('Closest li:', closest);
console.log('Data on closest li:', closest.tagName, closest.dataset && closest.dataset.id || 'no data');
// alternative
console.log(' === alt ===');
let elem = item.target;
while (elem.tagName.toLowerCase() !== 'li') {
elem = elem.parentNode;
if (elem.tagName.toLowerCase() === 'ul') {
break;
}
}
console.log('Manual version:', elem.tagName, elem.dataset && elem.dataset.id || 'no data');
});
console.log('Listening.');
}
attachClickHandler();
<h1>My list</h1>
<ul class="list">
<li data-id="1">Item 1</li>
<li data-id="2"><button>Item 2</button></li>
<li>Empty item</li>
</ul>
Edit: This won't work on IE as stackoverflow likely doesn't load the polyfill. But if you test it standalone (should be 3 seconds work) you can verify if it's good enough for you.
I have a piece of code that closes a drop down menu, if you click somehwere on the document other than the opened menu itself. I would like to get rid of jQuery, but I'm not sure how to translate this code to pure javascript.
$(document).ready(function() {
$(document).click(function(event) {
if (!$(event.target).closest('li.main').length) {
if ($('li.main').is(":visible")) {
$('#dropdown').hide();
}
}
})
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id="main" onclick="ToggleMainMenu();"><span>All categories</span>
</li>
<li> Item 1
</li>
<li> Item 2
</li>
<li> Item 3
</li>
</ul>
Your click handler is doing the following:
if ( clicked object is a descendent of "li.main" AND "li.main" is visible)
hide "#dropdown"
If you add the onclick attribute to the node "li.main" or one of its descendents - as you added ToggleMainMenu in your comment - then you guarantee:
- The clicked item is "li.main" or is a descendent of "li.main"
- "li.main" is visible (since you cannot click an invisible item)
At this point you do not need comparisons any more and you only need to hide "#dropdown" in ToggleMainMenu function's body. Copying from javascript hide/show element :
document.getElementById("dropdown").style.display = "none";
p.s. I used jquery selector notation for brevity
Sometimes it's easier than you think. I found a solution:
document.addEventListener("DOMContentLoaded", function (event) {
document.addEventListener('click', function (e) {
var mainMenu = document.getElementById('main');
if (!mainMenu.contains(e.target)) {
document.getElementById('dropdown').style.display = 'none';
}
}, false);
});
I didn't know about contains.
I tried asking this before, but I guess I wasn't specific enough. Suppose I have HTML code that looks like this. How do I ONLY target the tags within the the horizontalNAV using pure JavaScript? Okay I know I could do this using jQuery like this...
<script type="text/javascript">
$(document).ready(function(){
$('#horizontalNAV li a').click(function(){
//jQuery code here...
});
});
</script>
However I do NOT want a jQuery answer, because I want to know how you target ('#horizontalNAV li a') using pure javaScript.
or you can tell me how to do it for the verticalNav portion, either way I'll get it, if I see an example or if its explained to me. If I'm not mistaken you would have to use the document.querySelectorAll method, if so, how does that work in the above example.
<div id="wrapper">
<div id="horizontalNav">
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
</div>
<div class="sideBar">
<div class="verticalNav">
<ul>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ul>
</div>
</div>
</div>
Without jQuery it would look like this
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
var elems = document.querySelectorAll('#horizontalNav li a');
for (var i = elems.length; i--;)
elems[i].addEventListener("click", handler, false);
}, false);
function handler(event) {
//javascript code here...
this.style.color = 'red';
}
</script>
FIDDLE
If #horizontalNAV is a UL or OL element, then it can only have LI element children so you can skip that part of the selector. The following doesn't use querySelectorAll so will work in browsers, that don't support it:
<script>
window.onload = function() {
var list = document.getElementById('#horizontalNAV');
var links = list && list.getElementsByTagName(‘a’);
if (links) {
for (var i=0, iLen=links.length; i<iLen; i++) {
links[i].onclick = listener;
}
}
}
function listener() {
// do stuff
}
</script>
If you want to include more than one listener for an event, you’ll need to use addEventListener or some other strategy instead of assigning the function directly to the element, but in most cases only one listener is required per event type and keeping things simple has its benefits.
The listener function is declared outside the function doing the assignment to avoid a closure and circular reference, so it should have less chance of creating a memory leak.
Suppose I have a List like the following
<ul>
<li id="slide-a" class="slide-li active-slide"><a href="#" >A</a></li>
<li id="slide-b" class="slide-li"><a href="#" >B</a></li>
<li id="slide-c" class="slide-li"><a href="#" >C</a></li
</ul>
Now , using Jquery I wanna Find out which Element has the class 'active-class'. One way would to have a nested if statement something like this:
if($("#slide-a").hasClass('active-slide'))
{
active = 'slide-a';
}
else
{
if($("#slide-b").hasClass('active-slide'))
{
active = 'slide-b';
}
else
{
if($("#slide-c").hasClass('active-slide'))
{
active = 'slide-c';
}
}
}
My question is if there exists any way to optimize the code above. Is there a generic way to achieve this such that even if I add 10 more li's in the ul the code just works fine without any modification.
Maybe just
var active = $(".active-slide").attr("id");
Demo
Use Attribute starts with selector and .each(). Try this:
$(document).ready(function(){
$('li[id^=slide]').each(function(){
if($(this).hasClass('active-slide'))
alert($(this).attr('id'));
});
});
DEMO
If you have more than one li with classactive-slide use this:
$(document).ready(function(){
var idVal = [];
$('li[id^=slide]').each(function(){
if($(this).hasClass('active-slide'))
idVal.push($(this).attr('id'));
});
console.log(idVal);
});
DEMO
You could a use jQuery $.each() on the ul to iterate through.
fiddle coming in a second.
I have little piece of javascript to show/hide divs based on which link is clicked in a list. Its not very eligant, but it works fine. What I'd like to do is to assign an active state the a list item depending on which div is showing. Here is my JS and HTML:
var ids=new Array('section1','section2','section3','section4');
function switchid(id){
hideallids();
showdiv(id);
}
function hideallids(){
//loop through the array and hide each element by id
for (var i=0;i<ids.length;i++){
hidediv(ids[i]);
}
}
function hidediv(id) {
//safe function to hide an element with a specified id
document.getElementById(id).style.display = 'none';
}
function showdiv(id) {
//safe function to show an element with a specified id
document.getElementById(id).style.display = 'block';
}
html:
<ul>
<li class="activeItem">One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
</ul>
<div id="section1" style="display:block;">1111111</div>
<div id="section2" style="display:none;">2222222</div>
<div id="section3" style="display:none;">3333333</div>
<div id="section4" style="display:none;">4444444</div>
When section 2 (or whichever) is clicked, I'd like the class "activeItem" to be removed from the li it is currently on and applied to the current li. Is this possible with javascript? I think it is, but I can't figure out how to implement it client side.
Thanks!
If you're able to use jQuery (or something similar) as it has this ability built in: http://docs.jquery.com/Attributes - addClass/removeClass
Change your anchors to use the onclick event instead of the href javascript code.
<a onclick="switchid('section1');return false;">One</a>
Then pass the argument this into your switchid function. this is a javascript keyword which in this scenario refers to the element (a).
<a onclick="switchid('section1', this);return false;">One</a>
Now to make the switchid function modify the list.
function switchid(id, el){
hideallids();
showdiv(id);
// rejiggered for text nodes
var li = el.parentNode.parentNode.childNodes[0];
while (li) {
if (!li.tagName || li.tagName.toLowerCase() != "li")
li = li.nextSibling; // skip the text node
if (li) {
li.className = "";
li = li.nextSibling;
}
}
el.parentNode.className = "activeItem";
}
Try it out: http://jsbin.com/esazu3/edit