(Click event) toggle and hide others JavaScript - javascript

I have an issue and I don't know how I can fix this and I need some help.
The following code I have so far
<div class="parent" data="one">
<div class="child hide" id="one">
test
</div>
</div>
<div class="parent" data="two">
<div class="child hide" id='two'>
test 2
</div>
</div>
var parent = document.getElementsByClassName("parent");
for (var i = 0; i < parent.length; i++) {
parent[i].addEventListener("click", function () {
var child = document.getElementsByClassName("child");
var attribute = this.getAttribute("data");
var the_element = document.getElementById(attribute);
for (var is = 0; is < child.length; is++) {
child[is].classList.add('hide');
child[is].classList.remove('show');
}
the_element.classList.add('show');
});
}
If the user click on the parent the child what is connected get the class show and the hide class is removed. If the user click on another parent all child elements get the class hide en the show class is removed. The code above works for this but what I also want is if the user clicks on the parent and after that the user clicks the same parent the class show remove and add hide at the child.
I think I must use this in JavaScript but how can I combine this all together?

You can toggle element using just adding 'hide' class. Add css classes as below.
<style>
.parent {
...
}
.child {
...
}
.hide {
display: none;
}
</style>
Add html elements as below.
<div class="parent" data="one">
<div class="child hide" id="one">
test
</div>
</div>
<div class="parent" data="two">
<div class="child hide" id='two'>
test 2
</div>
</div>
Then add javascript to toggle elements.
<script>
// Get parent elements and children elements
let parents = document.getElementsByClassName("parent");
let children = document.getElementsByClassName("child");
// Form an Array with parent elements and
// add event listener for each parent
Array.from(parents).forEach(parent => {
parent.addEventListener("click", function () {
// Form an Array with child elements and
// check condition for each children
Array.from(children).forEach(child => {
// Check wheather is it the child of clicked parent
if(child.parentNode == this) {
// If clicked parent's child just toggle hide class
child.classList.toggle("hide");
}else {
// Add hide class for all other children
child.classList.add("hide");
}
});
});
});
</script>
Thats it! Hope it helps.

Related

Javascript nested collapsible divs

I am creating a collapsible div, which has another collapsible div nested inside:
<button class="collapsible">Expand First Panel</button>
<div class="firstPanel">
<div class="fistPanelContent"> ••• </div>
<button>Expand Second Panel</button>
<div class="secondPanel">
Content of the second panel
</div>
</div>
On click of the first button, the firstPanel div should expand, and upon clicking the nested second button, the secondPanel div should expand.
I've managed to achieve a single expanding div by using overflow: hidden; and a script to change the div's max height:
<script>
var allCollapsibles = document.getElementsByClassName("collapsible");
// Iterate through the collapsibles
var index;
for (index = 0; index < allCollapsibles.length; index++) {
// Add expansion toggle
allCollapsibles[index].addEventListener("click", function() {
// Set max height for collapsible element
var expandableContent = this.nextElementSibling;
if (expandableContent.style.maxHeight) {
expandableContent.style.maxHeight = null;
} else {
expandableContent.style.maxHeight = expandableContent.scrollHeight + "px";
}
});
}
</script>
... but this won't work for the second expanding div, because I've set the first div's max height
How could I get the second div to expand, whilst it's inside the first div?
This is a solution for your markup. It is much simpler to just add and remove a class like show instead of going down the road and doing height calculations. You can then add or remove the class show depending if the item already has the class or not. With css you can hide or show the item.
var allCollapsibles = document.querySelectorAll('.collapsible');
allCollapsibles.forEach( item => {
item.addEventListener("click", function() {
if(this.nextElementSibling.classList.contains('show')) {
this.nextElementSibling.classList.remove('show')
} else {
this.nextElementSibling.classList.add('show')
}
});
});
.firstPanel,
.secondPanel {
display: none;
}
.firstPanel.show,
.secondPanel.show {
display: block;
}
<button class="collapsible">Expand First Panel</button>
<div class="firstPanel">
<div class="fistPanelContent"> ••• </div>
<button class="collapsible">Expand Second Panel</button>
<div class="secondPanel">
Content of the second panel
</div>
</div>

Set parent style from child element [duplicate]

This question already has an answer here:
Preventing event bubbling
(1 answer)
Closed 1 year ago.
I'm trying to create a custom dropdown field. Following is my code:
const list = document.querySelector('.list');
const listItems = list.getElementsByTagName('p');
document.querySelector('#category').onclick = function() {
// Able to show the .list DOM element
list.style.display = 'block';
}
Array.from(listItems)
.forEach(function(listItem) {
listItem.onclick = function() {
// Unable to hide the .list DOM element
list.style.display = 'none';
// .listItem's value is getting logged
console.log(listItem.getAttribute('value'));
}
}
);
<div class="dropdown" id="category" name="category">
<div class="trigger">
<p class="selected-category">Category</p>
</div>
<div class="list">
<p value="">None</p>
<p value="1">harum inventore</p>
<p value="2">dolorem voluptatem</p>
<p value="3">dolores consectetur</p>
<p value="4">velit culpa</p>
<p value="5">beatae nulla</p>
</div>
</div>
I'm not exactly sure where it is going wrong as I'm able to set the list's style attribute to to display: block but I'm unable to set it back to display: none even though I'm trying to do the same thing from two different places.
I'm pretty new to Javascript. This might be a duplicate but I honestly tried looking up and trying as many solutions as possible. But none seemed to work. So apologies in advance.
Your code was working right, but because your parent and child had click event listeners on them, you were hiding and showing the list back.
Here you can find more about event bubbling: https://javascript.info/bubbling-and-capturing
const list = document.querySelector('.list');
const listItems = list.getElementsByTagName('p');
// Overlapping event listener 1
document.querySelector('#category').onclick = function() {
list.style.display = 'block';
}
Array.from(listItems)
.forEach(function(listItem) {
// Overlapping event listener 2
listItem.onclick = function(event) {
// to fix double event issue, you have to prevent its bubbling up
event.stopImmediatePropagation();
list.style.display = 'none';
// Uncomment the debugger here and you will see that this code works right
// debugger;
console.log(listItem.getAttribute('data-value'));
}
}
);
<div class="dropdown" id="category" name="category">
<div class="trigger">
<p class="selected-category">Category</p>
</div>
<div class="list">
<p data-value="">None</p>
<p data-value="1">harum inventore</p>
<p data-value="2">dolorem voluptatem</p>
<p data-value="3">dolores consectetur</p>
<p data-value="4">velit culpa</p>
<p data-value="5">beatae nulla</p>
</div>
</div>
The event listener of your parent #category element is executed every time you click one of it's child elements. You can prevent the click event from bubbling up to the parent element by using the stopPropagation() method on the child elements' listener.
Also, as #connexo mentioned in the comments, <p> elements do not have a value attribute. You are probably looking for a <ul> element with <li> children.
Check and run the following Code Snippet for a practical example of the above approach:
const list = document.querySelector('.list');
const listItems = document.querySelectorAll('.list li');
document.querySelector('#category').onclick = function() {
list.style.display = 'block'; // Able to show the .list DOM element
}
Array.from(listItems)
.forEach(function(listItem) {
listItem.onclick = function(e) {
e.stopPropagation(); // prevent parent event listener from being executed
list.style.display = 'none'; // Unable to hide the .list DOM element
}
}
);
.list {display: none; list-style: none;}
<div class="dropdown" id="category" name="category">
<div class="trigger">
<p class="selected-category">Category</p>
</div>
<ul class="list">
<li>None</li>
<li>harum inventore</li>
<li>dolorem voluptatem</li>
<li>dolores consectetur</li>
<li>velit culpa</li>
<li>beatae nulla</li>
</ul>
</div>

Testing if a click is in a div and all its children

I am facing an issue about this.
<div id="1">
<div id="2">
</div>
<div id="3">
<div id="4">
</div>
</div>
</div>
<div id="others_div">
</div>
I want to add the class "hidden" to "1" when I click on something which is not "1" nor one of its children.
Now I am using this but I have a lack of imagination for solving this issue...
document.onclick = function(e)
{
if(e.target.id!="1")
{
$("#1").addClass("hidden");
}
}
Well, to avoid e.stopPropagation() (maybe you want that event to bubble up to some other ancestor) You can check if it is not clicked on #1 nor on it's children like this:
$('body').on('click', function(e) {
if (!((e.target.id== "1") || $(e.target).closest('#1').length)) {
$("#1").addClass("hidden");
}
});
You could use a jQuery check like the following one to check if the current element is your 1 element or traverse the DOM to see if the current target is contained within an element with an ID of 1 :
<script>
$(function(){
// Trigger this when something is clicked
$(document).click(function(e){
// Toggle the hidden class based on if the current element is 1
// or if it is contained in an element with ID of 1
$("#1").toggleClass('hidden',!((e.target.id== "1") || $(e.target).closest('#1').length))
});
});
</script>
Generally, you should avoid using ID attributes that only consists of numbers as they are not valid (ID attributes must begin with a letter). Ignoring this could result in some issues with regards to CSS or jQuery selection.
JQuery
$('body').on( "click", function(e) {
if(e.target.id !== "1")
{
$("#1").addClass("hidden");
}
});
I think you want this
// taken from http://stackoverflow.com/questions/152975/how-to-detect-a-click-outside-an-element
$('html').click(function() {
//Hide the menus if visible
alert('hide');
});
$('#one').click(function(event){
event.stopPropagation();
});
div#one {
background: yellow;
}
div#others_div {
background: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<div id="one">
div one
<div id="2">
div two
</div>
<div id="3">
div three
<div id="4">
div four
</div>
</div>
</div>
<div id="others_div">
other div
</div>

How to add slide animation when showing elements

I would like to add animation effect to following code when showing tree items.
I know that jquery has slide functions, and css has "transition", but not sure how to apply these to my code. Any ideas?
<head>
<script language="JavaScript">
function show(){
var elements = document.getElementsByClassName("label");
for(var i = 0, length = elements.length; i < length; i++) {
elements[i].style.display = 'block';
}
}
</script>
<style>
.label {
-webkit-padding-start: 20px;
display: none;
}
</style>
</head>
<body>
<div>
<div onclick="show()">1st Row</div>
<div>
<div class="label">First</div>
<div class="label">Second</div>
<div class="label">Third</div>
</div>
<div>2nd Row</div>
</div>
</body>
If you are planning to use jQuery then you can use slideDown and slideUp method to show/hide elements with animation. There is slideToggle method which alternatively show/hides the element with animcation. You can modify your show method as below
Working demo
function show(obj){
var $this = $(obj);//Here obj points to the element clicked
//Now you have to show/hide the next sibling of the element clicked
//We will use next() method which gives the next sibling of element
//And then call slideToggle on it to show/hide alternatively
$this.next().slideToggle();
}
Change in the markup
<div onclick="show(this)">1st Row</div>
function show() {
$('.label').slideDown();
}
This selects all elements with the .label class and slides them into view. There is also a .fadeIn() function.
Also, you can attach click handlers by selectors (like an id or class):
<div>
<div class="row">1st Row</div>
<div>
<div class="label">First</div>
<div class="label">Second</div>
<div class="label">Third</div>
</div>
<div class="row">2nd Row</div>
</div>
Notice I removed the onClick="" statement and added a class to the row div. Then you can select the element you want to attach the click event to and keep all the code in one place:
$('.row').bind('click', function () {
$(this).next().find('.label').slideToggle();
});
This JavaScript above adds a click handler to all elements with the row class and toggles the display of all of the elements with the label class in the next element.
Here is a jsfiddle: http://jsfiddle.net/L34g3/.

Select next <div /> once you know the closest selector with javascript

I am developing a website and I can't use jQuery (no discussion about this), so pure javascript and a custom javascript framework is used.
Actually I have found a situation that I don't know how to handle:
I've a group of selectors, that for each one I add a "onclick" event to display / hide a div.
For example:
<div id="menu">
<div class="menu-item">
<div class="arrow">
<a class="down">Open / Close</a>
</div>
Menu Item
<div class="extramenu hidden">
Extra menu items
</div>
</div>
<div class="menu-item">
<div class="arrow">
<a class="up">Open / Close</a>
</div>
Menu Item 2
<div class="extramenu">
Extra menu items
</div>
</div>
<div class="menu-item">
<div class="arrow">
<a class="down">Open / Close</a>
</div>
Menu Item 3
<div class="extramenu hidden">
Extra menu items
</div>
</div>
</div>
I select all "div.menu-item .arrow a" items, so I've 3 items. For each item I add a onclick event (that actually works fine).
What I need to archive is how to select the "closest" class .extramenu inside the div.menu-item. Then detect if the <a /> have a class .up or .down and if class == .up, add the class hidden; and if class == .down, remove the class hidden.
This a concept of what have to do, it's not javascript code:
var elements; // my list of elements
each(elements, function(element) {
// here element is pointing to the ANCHOR
add_event(element, "onclick", function(e) {
var submenu; // here I need to detect the submenu closest to my anchor
var state; // here I need to know if the anchor has class up or down
if (state == "up")
{
add_class(submenu, "hidden"); // hide the submenu div
remove_class(element, "up"); // remove the class up
add_class(element, "down"); // and add the class down
}
else if (state == "down")
{
remove_class(submenu, "hidden"); // remove the class to show the menu
remove_class(element, "down"); // remove the class down
add_class(element, "up"); // and add the class up
}
});
});
Thank you guys and sorry if it's not well explained, I did my best!
element.querySelectorAll allows you to select elements by CSS selector.
element.classList allows you to access the classes of an element
add_event(element, "onclick", function(e) {
var el = e.target, state;
var parent = el.parentNode;
while (!parent.classList.contains('menu-item')) {
parent = parent.parentNode;
}
var submenu = parent.querySelector('extramenu');
if (el.classList.contains('up')) {
state = 'up';
} else {
state = 'down'
}
/* ... */
});
You can write the rest of the pseudo code yourself.
I'm assuming your already using Modernizr for supporting legacy browsers like IE8. If your not, then do so.
Maybe not exactly the way you want to do this but if class up or down would be added to parent of the a ie div.arrow you could do all of the hiding/showing with css combinator +. like this:
.arrow.down + .extramenu {
/* the same styles as with hidden class */
}
This is the code you are looking for. Tested in FF5, should be cross-browser.
// use in your add_event function
var submenu = element.parentNode.nextSibling;
while(submenu && (submenu.nodeName!="DIV" || submenu.className.indexOf("extramenu")==-1)) submenu = submenu.nextSibling;
var state = element.className;

Categories