This is my javascript and it works great except that if you click the same link twice it toggles. How can I keep that from happening? Ultimately I just want to show a psection based on item clicked... but if you click it twice it toggles.
current = "intersitial"; // div with id="m0" is currently diplayed
function show_or_hide ( id )
{
if ( current ) //if something is displayed
{
document.getElementById ( current ).style.display = "none";
if ( current == id ) //if <div> is already diplayed
{
current = 0;
}
else
{
document.getElementById ( id ).style.display = "block";
current = id;
}
}
else //if nothing is displayed
{
document.getElementById ( id ).style.display = "block";
current = id;
}
}
My HTML is:
<ul>
<li onclick="show_or_hide('intersitial')"><span>intersitial</span></li>
<li onclick="show_or_hide('advancedDetail')"><span>advancedDetail</span></li>
<li onclick="show_or_hide('ultimateDetail')"><span>ultimateDetail</span></li>
</ul>
<div class="megamenu" id="intersitial">intersitial</div>
<div class="megamenu" id="advancedDetail" style="display: none">advancedDetail</div>
<div class="megamenu" id="ultimateDetail" style="display: none">ultimateDetail</div>
I'd suggest changing from the obtrusive JavaScript (using in-line event-handlers, onclick, onfocus, onblur and so on), and instead using JavaScript to bind the events:
// use a function-name that's descriptive of what it does:
function showOnly() {
// or you could use `document.getElementsByClassName('megamenu'):
var divs = document.querySelectorAll('div.megamenu'),
// gets the text from the 'span' of the clicked 'li' (the 'id' for later):
id = this.firstChild.textContent;
// iterates over each of the found '.megamenu' elements:
for (var i = 0, len = divs.length; i < len; i++){
/* if the id of the current 'div' is the same as the text in the 'span'
display = block, otherwise display = none:
*/
divs[i].style.display = divs[i].id === id ? 'block' : 'none';
}
}
// get the 'li' elements:
var lis = document.querySelectorAll('li');
// iterate over those elements and bind an event-handler to them:
for (var i = 0, len = lis.length; i < len; i++) {
lis[i].addEventListener('click', showOnly);
}
JS Fiddle demo.
This approach also avoids littering the global namespace with variables (which are easily over-written inside of other functions, particularly (but not exclusively) when multiple developers work on the same project).
References:
Element.addEventListener().
document.querySelectorAll().
Node.firstChild.
Node.textContent.
function show_or_hide(id)
{
if (current == id) return;
if (current) // if something is displayed
{
document.getElementById ( current ).style.display = "none";
}
document.getElementById ( id ).style.display = "block";
current = id;
}
Related
I am writing Javascript code that shows pictures on a category based on the ones the user clicked on but will show all categories when clicked outside the filtered pictures. Yet, I need the same code to work on separate divs independently, not the whole dom.
Attaching the event listener to the document works except, as you may have guessed, it doesn't work on two divs independently. When I attach it to a reference dom, let's day the dom id, it works but it doesn't know when the user clicked outside
document.addEventListener("click", (evt) => {
//get an array of all the div with "column" class
var imgElements = imgGrid.getElementsByClassName("column");
var w, x;
var y, z;
let targetElement = evt.target; // clicked element
do {
//itirate through all the divs we got the reference for
for (var i = 0; i < imgElements.length; i++) {
//check if we click on any of those divs
if (targetElement.className == imgElements[i].className) {
//we clicked on a div
//let's get the class name we want to filter by
w = imgElements[i].className;
x = w.split(' ');
console.log("You clicked on a: " + x[1]);
//we're not done //let's go itirate those divs once more
//but this time for everyone that don't have a class that
//matched our filter class we hide it, else we show it
for (var i = 0; i < imgElements.length; i++) {
y = imgElements[i].className;
z = y.split(' ');
if (z[1] != x[1]) {
addClass(imgElements[i], "hidden");
} else {
removeClass(imgElements[i], "hidden");
}
}
return;
}
}
// Go up the DOM.
targetElement = targetElement.parentNode;
} while (targetElement);
console.log("You clicked outside");
//other useful things being done here
});
Instead of "document.addEventListener" I will say DomId.addEventlistner and expect it to know to when I clicked inside and outside of each dom reference.
You can give DIVs that should be 'clickable' an unique class name. That way your logic doesn't have to know each 'inside' DIVs id.
function handleClick(e) {
if (e.target.className != "clickableDiv") {
console.log("clicked outside");
} else {
var element = e.target.id;
console.log(element + " clicked");
}
}
document.addEventListener("click", handleClick);
<div id="container1" class="clickableDiv">
DIV1
</div>
<div id="container2" class="clickableDiv">
DIV2
</div>
<div id="container3">
I'm not clickable
</div>
I'm trying to apply the onclick event with JavaScript to the following elements:
<div class="abc">first</div>
<div class="abc">second</div>
<div class="abc">third</div>
If I click on the first element (with index [0]) then this works, but I
need this event applicable for all classes:
document.getElementsByClassName('abc')[0].onclick="function(){fun1();}";
function fun1(){
document.getElementsByClassName('abc').style.color="red";
}
.onclick does not expect to receive a string, and in fact you don't need an extra function at all.
However, to assign it to each element, use a loop, like I'm sure you must have learned about in a beginner tutorial.
var els = document.getElementsByClassName('abc');
for (var i = 0; i < els.length; i++) {
els[i].onclick = fun1;
}
function fun1() {
this.style.color = "red";
}
<div class="abc">first</div>
<div class="abc">second</div>
<div class="abc">third</div>
To expand on the solution provided by #rock star I added two small additions to the function. First it is better to add / reemove a class (with an associated style rule) to an element than directly applying the stylerule to the element.
Secondly - on the click event - this will now remove the red class (and therefore style) from the previously selected element and add it to the new element. This will allow only one element to be red at a time (in the original solution any element that was clicked would become red).
var els = document.getElementsByClassName('abc');
for (var i = 0; i < els.length; i++) {
els[i].onclick = fun1;
}
function fun1() {
var oldLink = document.getElementsByClassName('red')[0];
if(oldLink) {oldLink.classList.remove('red')};
this.classList.add('red');
}
.red {
color:red;
}
<div class="abc">first</div>
<div class="abc">second</div>
<div class="abc">third</div>
This works:
<body>
<div class="abc">first</div>
<div class="abc">second</div>
<div class="abc">third</div>
<script>
var elements = document.getElementsByClassName('abc');
for(var i = 0, max = elements.length; i < max; i += 1) {
var clickedElement = elements[i];
clickedElement.onclick=function (){
fun1(this);
};
}
function fun1(element){
element.style.color="red";
}
</script>
</body>
Need to print the all parentnodes nodename in order when we clicked anywhere in a html page. for eg if i clicked somewhere (inside the html page) paragraph. i need output as HTML>Body>Div>p>(clicked)
I need to use only simple javascript for this. I tried the below code. but i can't get answer. help me to findout,
script.js
document.body.onclick = function(e){
var x= e.parentNode.nodeName;
console.log(x);
};
Once you have the event target, you just climb up the parentNode tree, prepending the tag names, e.g.
window.onload = function(){
document.body.addEventListener('click', climbNodes);
}
function climbNodes(e){
var node = e.target;
var ancestors = node.tagName;
while (node.parentNode && node.parentNode.tagName) {
node = node.parentNode;
ancestors = node.tagName + '>' + ancestors;
}
console.log(ancestors);
}
<div>Click here
<div>or here
<p>
<span>or here</span>
</p>
<ul>
<li>or here</li>
</ul>
</div>
</div>
let els = document.getElementsByTagName('*')
let nodes = []
for (let i = 0; i < els.length; i++) {
els[i].addEventListener('click', function() {
event.stopPropagation();
let element = this
nodes = []
while (element.nodeName !== 'HTML') {
// Add element to nodes
nodes.push(element)
// Set element equal to parent element
element = element.parentNode
}
})
}
I'm adding a class to an element and want to remove it from siblings. In JQuery it is simple, but how to do it the best way in plain JS?
Here is an example of my code.
<div class="controllers">
<span id='one' class='active'></span>
<span id='two'></span>
<span id='three'></span>
</div>
firstBtn.onclick = function() {
slides[0].className = 'slide active';
this.className = 'active';
};
You can use loop inside click event to remove active class from all elements and then again set it on clicked element.
var el = document.querySelectorAll('.controllers span');
for (let i = 0; i < el.length; i++) {
el[i].onclick = function() {
var c = 0;
while (c < el.length) {
el[c++].className = 'slide';
}
el[i].className = 'slide active';
};
}
.active {
color: red;
}
<div class="controllers">
<span id='one' class='active'>Lorem</span>
<span id='two'>Lorem</span>
<span id='three'>Lorem</span>
</div>
A generic function to set only one active element would be:
const setActive = el => {
[...el.parentElement.children].forEach(sib => sib.classList.remove('active'));
el.classList.add('active')
}
In your example:
let spans = [...document.body.querySelectorAll('.controllers > span')];
spans.forEach(el => el.addEventListener('click', e => setActive(el)))
Fiddle: https://jsfiddle.net/9j1qguac/
Update: At one point, browsers expected Array.from(el.parentElement.children) instead of [...el.parentElement.children]. Both are now supported, but if you start a row with a bracket, the previous row needs a semicolon. (eg a = b \n [1].map... will be treated as a = (b[1]).map....
To remove a class from sibling
var el = document.getElementById( "two" );
var one = el.previousSibling();
one.classList.remove("active");
use previousSibling or nextSibling to traverse to it, and use classList.remove to remove a class.
Remove a class from an element siblings
let elem = document.getElementById('id');
let siblings = elem.parentElement.children;
for(let sib of siblings) {
sib.classList.remove('active')
}
<div id="tab1" class="nav left">
<ul>
<li>Home</li>
......
</ul>
</div>
Now, i want to remove the class="now" or set the class value empty. If the url not on mangento, I using the following code. But the I don't know how to write the last part.
window.onload = function removeNow() {
var div = document.getElementById("tab1").getElementsByTagName("a");
if (window.location.pathname != '/magento/') {
div.removeClass();
}
}
Thank you.
In modern browsers you can use the classList API:
div.classList.remove( 'now' );
But a problem specific to your code: You must loop in order to remove the class. So try this:
for ( var i = 0; i < div.length; i++ ) {
div[i].classList.remove( 'now' );
}
If your browser doesn't support classList, use this removeClass shim:
function removeClass( elem, name ) {
var classlist = elem.className.split( /\s/ ),
newlist = [],
idx = 0;
for ( ; idx < classlist.length; idx++ ) {
if ( classlist[ idx ] !== name ) {
newlist.push( classlist[ idx ] );
}
}
elem.className = newlist.join(" ");
return true;
}
or with jQuery (with which we are not required to use classList or className):
$('a').each(function() {
if (window.location.pathname != '/magento/')
$(this).removeClass();
});
Set the className property:
div.className = '';
Note that getElementsByTagName returns a (possibly empty) NodeList, so:
var div = document.getElementById("tab1").getElementsByTagName("a");
is a collection of all the A element descendents of the element with ID "tab1" (and so 'div' is probably not a good name).
If all you want to do is remove all class values of the first such A element, then:
div[0].className = '';
will do the job. But since the NodeList might be empty, the following would be more robust:
if (div[0]) {
div[0].className = '';
}
or perhaps
div[0] && div[0].className = '';
it depends on your coding style and maintainability requirements.