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.
Related
I am working with this:
<html>
<ol onclick="myevent(event);">
<li title="a">Test 1</p>
<li title="b">Test 2</p>
</ol>
<div id="a" style="display:none;">Text to show</div>
<div id="b" style="display:none;">Other text to show</div>
<script>
function myevent(event) {
var x, i, clk, res;
x = document.getElementsByTagName("DIV");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
clk = event.target.title;
res = document.getElementById(clk);
res.style.display = "block";
}
</script>
</html>
Essentially click one line item, hide all blocks, and then show the named block. This should be simple but I have been researching for hours and getting nowhere.
Thanks in advance!
- Joe
You have some typos in your code.
Firstly, you are wrongly closing li element with </p>
Secondly, getElementsBytagname should be getElementsByTagName and getelementbyid should be getElementById:
function myevent(event){
var x, i, clk, res;
x = document.getElementsByTagName("DIV");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
clk = event.target.title;
res = document.getElementById(clk);
res.style.display="block";
}
<ol onclick="myevent(event);">
<li title="a">Test 1</li>
<li title="b">Test 2</li>
</ol>
<div id="a" style="display:none;">Text to show</div>
<div id="b" style="display:none;">Other text to show</div>
You can also try using querySelectorAll() and forEach() that does not require the loop variable i:
function myevent(e){
var x, clk, res;
x = document.querySelectorAll("DIV");
x.forEach(div => div.style.display = "none");
clk = e.target.title;
res = document.getElementById(clk);
res.style.display="block";
}
<ol onclick="myevent(event);">
<li title="a">Test 1</li>
<li title="b">Test 2</li>
</ol>
<div id="a" style="display:none;">Text to show</div>
<div id="b" style="display:none;">Other text to show</div>
Event Handlers
There's three ways to handle an event (in this case the click event on <ol>):
Event Attribute (not recommended)
<ol onclick="eventHandler(); return false">...</ol>
Event Property
document.querySelector('ol').onclick = eventHandler;
Event Listener (recommended)
document.querySelector('ol').addEventListener('click', eventHandler);
For details refer to Introduction to Events and DOM Onevent Handlers
Event Delegation
The following demo uses a programming paradigm called Event Delegation. The advantages over the code provided in OP (Original Post) are:
Only one tag needs to be registered as the event listener (common ancestor tag of all targeted tags - ex. <ol> is an ancestor tag to all <li>).
There's no need to loop thru each targeted tag and register each one to an event (ex. <li> do not have to be in a for loop).
There can be an unlimited number of targeted tags and knowing how many is not required. (ex. there can be a single <li> to theoretically thousands of <li>).
The targeted tags can be added dynamically anytime -- during or after page load. If done any other way, any dynamically added tags would not work without reloading the page and somehow keeping them.
Refer to Event Delegation for details.
References
In the following demo these methods and properties were used:
DOM
Document.querySelector()
Element.tagName
Node.textContent
Event
EventTarget.addEventListener()
Event.currentTarget
Event.target
Event.stopPropagation()
Demo
Details are commented in demo
/* EVENT DELEGATION */
// Register Ancestor Tag to Event
/*
Register the click event to a common ancestor tag of all
tags you want to be clickable.
So in this instance the clickable tags are all <li>
Ancestor tags could be:
window
document
<body>
<main>
<ol>
Usually the closest tag is the best choice which is <ol>
*/
// ------ Breakdown ------
/*
document.querySelector('ol')
-- find <ol>
.addEventListener('click'...
-- register click event to <ol>
..., eventHandler);
-- run function eventHandler() when <ol> is clicked
*/
document.querySelector('ol').addEventListener('click', eventHandler);
// Event Object
/*
Pass event object which will have properties and methods that will allow us to:
1. find what tag is listening for click event
- event.currentTarget
- in this demo it is <ol>
2. find what tag the user actually clicked
- event.target
- in this demo only <li> will react to being clicked
3. stop the click event from "bubbling" up the event chain
- event.stopPropagation();
- this method is called at the end of eventHandler() so
that the click event doesn't trigger anything else
*/
function eventHandler(event) {
// Reference all tags concerned
// See Event Object #1
const listener = event.currentTarget;
// See Event Object #2
const clicked = event.target;
// Find <output>
const display = document.querySelector('output');
// ------ Breakdown ------
/*
if <ol> isn't the tag that the user clicked...
... and if the clicked tag is a <li>...
... get the text inside that <li>...
... and place it in <output>
(it gets overwritten on each click so there's no need for multiple tags)
*/
if (listener !== clicked) {
if (clicked.tagName === 'LI') {
let data = clicked.textContent;
display.textContent = data;
}
}
/*
Regardless of whether any <li> were clicked stop the click event
See Event Object #3
*/
event.stopPropagation();
}
main {
font: 700 1rem/1.5 Tahoma
}
li:hover {
cursor: pointer
}
<main>
<ol>
<li>A</li>
<li>B</li>
<li>C</li>
<li>D</li>
<li>E</li>
<li>F</li>
</ol>
Item: <output></output>
</main>
I am currently developing a program. It includes a 3 option navigation bar. It uses <li> and does not have id's, when i try to add id's to them it messes up the order, and doesent even work with a click! Im starting to loose faith with it.. can anyone help me on this one,
my GOAL is to have it alert different things on different clicks, so than I could link different html pages,
fiddle used HERE.
<ul class="ui-module menu-selector" id="menu-selector">
<li>Home</li>
<li class="js-is-active">Notif's</li>
<li>Profile</li>
</ul>
Since you don't have ids, I suppose that childNodes property will help a lot.
For example, you can use:
var lis = document.getElementById('menu-selector').childNodes;
// or you can select lis directly...
// var lis = document.querySelectorAll('#menu-selector li');
Array.prototype.slice.call(lis)
.forEach(function(li) {
// do something... like
li.onclick = function () {
console.log(this);
}
});
Note: childNodes (or querySelectorAll return) is NodeList type, and I use Array.prototype.slice.call() in order to use forEach() method on it.
See childNodes for more details.
if you don't want to have ids on your li elements for some reason you can use the following logic to select active li:
$("#menu-selector li.active").on("click", function(){
alert($(this).text())
});
I added id's for you, not sure what you meant by it messing up the order.
HTML
<div class="ui-items">
<header class="ui-module app-header">VoiceBox <i class="entypo-user-add"></i>
<i class="entypo-pencil"></i>
</header>
<div id="outer">
<ul class="ui-module menu-selector" id="menu-selector">
<li id="home_li">Home</li>
<li id="notif_li" class="js-is-active">Notif's</li>
<li id="profile_li">Profile</li>
</ul>
</div>
</div>
Javascript
var listItem = $('#menu-selector > li');
$(listItem).click(function() {
$(listItem).removeClass('js-is-active');
$(this).toggleClass('js-is-active');
});
$('#home_li').click(function(){
alert('home clicked')
})
$('#notif_li').click(function(){
alert('notifs clicked')
})
$('#profile_li').click(function(){
alert('profile clicked')
})
Fiddle http://jsfiddle.net/1swep9oq/2/
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
I've read many tutorials and can't seem to get it right. Ok I know that the jquery click function works when you are doing something to the exact same element but how do you make it effect another and toggle back?
I have a menu and when I click on an item I want the background (body) to change to an image.
Example:
HTML
<body>
<ul class="menu">
<li class="menu-item"><a>item 1</a></li>
<li class="menu-item"><a>item 2</a></li>
<li class="menu-item"><a>item 3</a></li>
</ul>
</body>
JQUERY
$(".menu-item a").click(function () {
$(body).css('background', 'http://example.com/image.png'); <-- first menu item
$(body).css('background', 'http://example.com/image.png'); <-- second menu item
$(body).css('background', 'http://example.com/image.png'); <-- third menu item
});
You can use .index() - DEMO
$("a").on("click", function(e) {
e.preventDefault();
var i = $("li").index( $(this).parent() );
if ( i === 1 ) {
$('body').css('background', 'beige');
} else if ( i === 2 ) {
$('body').css('background', 'honeydew');
} else {
$('body').css('background', 'pink');
}
});
Does this seem about like what you're trying to do?
$(".menu-item a:nth-child(1)").click(function () { // first menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(2)").click(function () { // second menu item
$(body).css('background', 'http://example.com/image.png');
});
$(".menu-item a:nth-child(3)").click(function () { // third menu item
$(body).css('background', 'http://example.com/image.png');
});
I don't know what you are trying but I could give you hints.
$(".menu-item a") // is an array/jquery collection of all elements that match the selector
.click(function () { // binds the function now to the click event of every item found
$(this); // is now the currently clicked element
// you can now traversal with $(this) for example
$(this).siblings(); // will be a collection of all surrounding elements
$(this).next(); // is the next element after the current one, can be chained like:
$(this).next().next(); // but I wouldn't recomand
$(this).prev(); // same as next but prev
$(this).parent(); // selects the direct parent element, in your case the li element
$(this).children(); // would select the direct children of the current element
// and so on.... there are much more possibilities
// on every of this possibilities you can do your background change
$("some selector"); // is of course still possible
// i think you are trying to do this:
var menuItems = $(".menu-item a");
menuItems.eq(0).css("background", "url to bg 1");
menuItems.eq(1).css("background", "url to bg 2");
menuItems.eq(2).css("background", "url to bg 3");
})
Look at the Traversing section of the jQuery docu. I would also always recommend to look what jQuery is actually doing. Many people hide behind jQuerys api and have no idea whats happening. This results into many misunderstandings.
You may try something like this
HTML:
<body>
<ul class="menu">
<li class="menu-item"><a name="blue" href="#">item 1</a></li>
<li class="menu-item"><a name="red" href="#">item 2</a></li>
<li class="menu-item"><a name="orange" href="#">item 3</a></li>
</ul>
</body>
CSS:
.red {
background:red;
}
.blue {
background:blue;
}
.orange {
background:orange;
}
Jquery:
$('.menu').on('click', 'a', function () {
var bgColor = $(this).attr('name');
$('body').removeClass().addClass(bgColor);
return false;
});
DEMO
The way I suggest going about this is to grab the position of the element that has been clicked. You can do this by using jQuery's index() function as other posters have suggested. Remember this will give you a zero-based index of the position which means that the position counting starts at 0 as opposed to 1.
Based on the item that has been clicked, you assign a CSS class to the target item which is the body element based on the sample code you provided.
Also, I noticed that your JS comments are still invalid even they were edited. Single line comments in JS use a double forward slash, // whereas multiline comments begin with /* and are closed by */.
This is my solution: http://jsfiddle.net/tgZUK/.
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