I want to toggle the visibility of classes of list items and also toggle the visibility of all the list items. With help of another stack overflow post, I am able to toggle specific classes of list items.
Here's the Javascript I have that toggles specific classes of list items:
var switcher = [false, false, false];
var classes = ['easy', 'fun', 'silly'];
$('.toggler').click(function () {
var x = $(this).hasClass('checked');
switcher[$(this).data('switch')] = !x;
$(this).toggleClass("checked", !x);
$('li').each(function () {
var cur = $(this);
cur.addClass('hidden');
$.each(switcher, function (index, data) {
if (data && cur.hasClass(classes[index])) {
cur.removeClass('hidden');
}
});
});
});
I added the basic functionality to hide and show all the list items, but the function brakes the individual class toggles:
$('.select_all').click(function () {
$(".toggler").toggleClass("checked");
$('li').toggleClass("hidden");
});
How can I keep class toggles, and add another button that toggles all the items?
Here's a fiddle example: http://jsfiddle.net/BMT4x/1/
I'm not sure if this is exactly what you're trying to do, but it should give you a push in the right direction (at least the direction I'd go). I prefer toggling everything with classes. Something like:
<button class="toggler" data-class="easy">Easy</button>,
<button class="toggler" data-class="fun">Fun</button>,
<button class="toggler" data-class="silly">Silly</button>,
<button class="toggler" data-class="all">Select All</button>
<ul id="list">
<li class="easy">Bowling</li>
<li class="fun">Balooning</li>
<li class="fun easy">Boating</li>
<li class="silly">Barfing</li>
<li class="easy fun">Bafooning</li>
</ul>
The CSS:
#list li {
display: none;
}
#list.easy li.easy {
display: block;
}
#list.fun li.fun {
display: block;
}
#list.silly li.silly {
display: block;
}
#list.all li {
display: block;
}
The JS:
$('.toggler').click(function () {
var category = $(this).data('class');
$('#list').toggleClass(category);
});
And a fiddle for reference: http://jsfiddle.net/qLLYj/
You can explicitly add/remove a class by passing a second, switch, parameter to toggleClass (see here).
So, you can change the state of all the individual switches and list items when clicking the .select_all button.
$('.select_all').click(function () {
$('.select_all').toggleClass("checked");
var allChecked = $('.select_all').hasClass("checked");
switcher = [allChecked, allChecked, allChecked];
$(".toggler").toggleClass("checked", allChecked);
$('li').toggleClass("hidden", !allChecked);
});
Some further changes made to get more intuitive behaviour
(e.g. if all checked, clicking one of the toggles deselects .select_all as well as itself; checking all individual toggles means .select_all is automatically checked):
$('.toggler').click(function () {
var x = $(this).hasClass('checked');
switcher[$(this).data('switch')] = !x;
$(this).toggleClass("checked");
$('li').each(function () {
var cur = $(this);
cur.addClass('hidden');
$.each(switcher, function (index, data) {
if (data && cur.hasClass(classes[index])) {
cur.removeClass('hidden');
}
});
});
var allChecked = switcher.indexOf(false) < 0;
$('.select_all').toggleClass("checked", allChecked);
});
Fiddle: http://jsfiddle.net/ET33B/
Related
^I would like to be able for the style to be enabled for only one at a time.
^I'm able to do this, which I don't want the user to be able to do.
So it's weirdly hard framing a question for what is possibly an easy solution. I basically have a list of build versions where I want the user to select one. When one of the versions are selected, it adds a border to the item to display that its clicked. However, with my code right now the user is able to select all 3 items and enable their CSS elements. I would like for the user to be able to only "activate" one item from the list.
HTML and CSS:
<ul class="listContents">
<li><p>Stable</p></li>
<li><p>Preview</p></li>
<li><p>LTS</p></li>
</ul>
<style>
.colorText {
background-color: #58a7ed;
color: white;
}
</style>
and the JS stuff:
const btn = document.querySelectorAll('.links');
for (let i = 0; i < btn.length; i++ ) {
btn[i].addEventListener("click", function() {
btn[i].classList.add('colorText')
})
}
I really hope I made myself clear, I feel like I'm failing my English trying to word this right lol.
You can also use a forEach loop, accessing the clicked link using event.target
const btns = document.querySelectorAll('.links');
btns.forEach(btn => {
btn.addEventListener('click', e => {
// remove any existing active links
btns.forEach(b => b.classList.remove('colorText'));
// activate the clicked link
e.target.classList.add('colorText');
})
});
.colorText {
background-color: #58a7ed;
color: white;
}
<ul class="listContents">
<li>
<p>Stable</p>
</li>
<li>
<p>Preview</p>
</li>
<li>
<p>LTS</p>
</li>
</ul>
Just before you add the colorText class to the desired item, we can remove colorText from ALL of them, ensuring that only 1 at a time gets the class.
// the rest is the same...
btn[i].addEventListener("click", function() {
// remove it from all:
btn.forEach(function(item) {
item.classList.remove('colorText');
});
// add it back to the desired one
btn[i].classList.add('colorText')
})
you can also use simple for of
const btn = document.querySelectorAll(".links");
for (let bt of btn) {
bt.addEventListener("click", (e) => {
btn.forEach((b) => b.classList.remove("colorText"));
e.target.classList.add("colorText");
});
}
hope everyone is keeping safe and coding a lot.
I am having a problem and have tried every solution but none are working. I do have a HAMBURGER MENU made of DIV's Elements, when open this menu will display all the MENU OPTIONS. So far it is working great but once the SECOND MENU is open I want to prevent the user to use the HAMBURGER MENU until the second menu is closed.
In my last try I have used POINTER-EVENTS: nome but believe it or not, it does stop the second menu from working and not the first one as it was supposed.
I will post here the code in JS and will be so grateful for any tips that will help me:
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById("#hamburger-menu").style['pointer-events'] = 'none';
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
});
It seems the issue is because you are adding other event listener inside the top one. You may need to separate them
document.querySelector('.hamburger-menu').addEventListener('click', () => {
document.querySelector('.nav-wrapper').classList.toggle('change');
});
document.querySelector('#close-window').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "none";
document.getElementById('.hamburger-menu').style.pointerEvents = "auto";
});
document.querySelector('#home-menu').addEventListener('click', () => {
document.getElementById("class-nav-1").style.display = "block";
document.getElementById(".hamburger-menu").style.pointerEvents = 'none';
});
I think you should leave the practice to control elements' behavior based on information gained from the DOM.
It would be much easier to control the behavior of your menu if you just toggled a boolean value in JS:
const btnFirst = document.getElementById('first')
const ddMenu = document.getElementById('second')
const ddItems = document.querySelectorAll('.dd-item')
const textArea = document.getElementById('third')
// this variable controls the behavior of the main menu
let btnFirstIsEnabled = true
btnFirst.addEventListener('click', function(e) {
if (btnFirstIsEnabled) {
btnFirstIsEnabled = false
ddMenu.classList.remove('hidden')
btnFirst.classList.add('disabled')
} else {
textArea.innerHTML = 'Cannot open dropdown again!'
}
})
ddItems.forEach(e => {
e.addEventListener('click', function(e) {
textArea.innerHTML = `Clicked: ${e.target.getAttribute('data-val')}`
ddMenu.classList.add('hidden')
btnFirstIsEnabled = true
btnFirst.classList.remove('disabled')
})
})
.main {
cursor: pointer;
}
.main.disabled {
color: #eaeaea;
}
.hidden {
display: none;
}
.dd-item {
cursor: pointer;
}
<div id="third">Clicked:</div>
<div id="first" class="main">OPEN</div>
<div id="second" class="hidden">
<ul>
<li class="dd-item" data-val="dd 1">Click dropdown item 1</li>
<li class="dd-item" data-val="dd 2">Click dropdown item 2</li>
<li class="dd-item" data-val="dd 3">Click dropdown item 3</li>
</ul>
</div>
I just added some coloring to the main menu in the snippet to show that it's not active, but the behavior is controlled by setting a Boolean variable in JS.
I think it would make your code much simpler if you made it work like this.
I have an off-canvas menu that flies out when a toggle-button is clicked - so far so good. All menu links share the same class name (in this case .nav-link). I need the menu to close when any of the links are clicked, I think have selected them all and I think I have to loop through an array of the selection but I'm unsure implement it. Right now nothing happens when a link is clicked.
My HTML:
<ul class="nav-list">
<li class="nav-item">articles</li>
<li class="nav-item">tags</li>
<li class="nav-item">links</li>
<li class="nav-item">archive</li>
</ul>
My CSS:
.nav-list {
margin: 0;
margin-top: 3.2em;
padding: 0;
background: #777;
width: 100%;
transform: translateX(-100%);
transition: transform 300ms cubic-bezier(.5, 0, .5, 1);
}
My JS:
const navToggle = document.querySelector('.nav-toggle')
const navLink = document.querySelectorAll('.nav-link')
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
navLink.addEventListener('click', () => {
document.body.classList.remove('nav-open')
})
You're trying to add an event listener to a collection of nodes (querySelectorAll
for .nav-link vs querySelector for .nav-toggle). You can either iterate over the collection and add your click event listener to each item or simply listen to the parent element of the .nav-links:
const navToggle = document.querySelector('.nav-toggle')
// replace this with something more sensible
const navLinkParent = document.querySelector('.nav-link').parentElement;
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
// this is adding a click listener to ONE element
navLinkParent.addEventListener('click', (event) => {
// check if the clicked element matches what you're after
if (event.target.classList.contains('nav-link')) {
document.body.classList.remove('nav-open')
}
})
You must use a loop because querySelectorAll returns an array.
const navToggle = document.querySelector('.nav-toggle')
const navLink = document.querySelectorAll('.nav-link')
navToggle.addEventListener('click', () => {
document.body.classList.toggle('nav-open')
})
for (var i = 0; i < navLink.length; ++i) {
navLink[i].addEventListener('click', () => {
if(navToggle.classList.contains('nav-toggle')){
(navToggle.classList.remove('nav-toggle');
}
});
}
If I've got multiple items that I want to change from a display of 'none', to a display of 'block', what's the most efficient way of doing it?
The JS I would use for a single item is below, but I imagine there are several on a page or site. Should I make use of function constructors somehow?
var sideNav = document.getElementById('sideNav');
var menuButton = document.getElementById('menuButton');
function toggle() {
if(sideNav.style.display) {
sideNav.style.display = '';
} else {
sideNav.style.display = 'block';
}
}
menuButton.addEventListener('click', toggle);
Take a look, see if this helps you.
I did it with vanilla JS, I don't know if you are currently using jQuery (would be easier if yes)
What I did:
Every button have it's own id that is used to "connect" to the elements that it should toggle.
First I add the listener to all buttons, passing it's id when the function is called.
Then in the function, I used document.querySelectorAll to get all elements with the class that should be hidden/show.
Then finally I run a loop in those elements, showing or not showing, depending on it's current 'display'.
var menuButtons = document.querySelectorAll('.menuButton');
menuButtons.forEach(function(btn){
btn.addEventListener("click", toggle.bind(this, btn.id));
})
function toggle(id) {
var sideNav = document.querySelectorAll('.nav_' + id);
sideNav.forEach(function(el){
if (el.style.display == 'none'){
el.style.display = "block";
} else {
el.style.display = "none"
}
})
}
div{
height: 30px;
width: 50px;
margin: 2px 0;
background: #999;
text-align: center
}
<button id="menuButton1" class="menuButton">Toggle 1</button>
<button id="menuButton2" class="menuButton">Toggle 2</button>
<button id="menuButton3" class="menuButton">Toggle 3</button>
<div class="nav_menuButton1">1</div>
<div class="nav_menuButton1">1</div>
<div class="nav_menuButton2">2</div>
<div class="nav_menuButton3">3</div>
<div class="nav_menuButton3">3</div>
<div class="nav_menuButton3">3</div>
Probably there are better approaches, but I'm now in a hurry and this is the best I could think in that moment
Use JQuery to obtain it:
$(document).ready(function(){
$('#menuButton').click(toggle);
});
function toggle(){
$('.toggle-item').each(function(){
$(this).show();
})
}
and for all you items, add the toggle-item class with this css:
.toggle-item{
display: none;
}
If for every button there is an item to show, this is the way:
$(document).ready(function(){
$('.menuButton').each(function(){
var button = $(this);
button.click(function(){
toggle(button.attr('data-target')));
});
});
});
function toggle(itemId){
$(itemId).show();
}
Adding this attribute to button:
<button class="menuButton" data-target="#toggle-item-1"></button>
Ok, so I really don't think I'm structuring my JS properly, and am hoping someone can point me in the right direction. I don't think sequentially naming the classes for each list item is an effective, or proper way of accomplishing this.
I have a list of checkboxes that, when clicked, need to perform two actions:
1) Toggle the class name of a div located directly below it.
2) Post via AJAX the state.
Here is my current markup:
<ol class="toolkitList">
<li>
<label><input class="inlineCheckers" type="checkbox" id="checkers" onchange="setCheck();" /><b id="grayTitle">List Title 1</b></label>
<i id="hiddenDiv">Div content can go here.</i>
</li>
<li>
<label><input class="inlineCheckers" type="checkbox" id="checkers2" onchange="setCheck2();" /><b id="grayTitle2">List title 2</b></label>
<i id="hiddenDiv2">Div content can go here.</i>
</li>
<li>
<label><input class="inlineCheckers" type="checkbox" id="checkers3" onchange="setCheck3();" /><b id="grayTitle3">List title 3</b></label>
<i id="hiddenDiv3">Div content can go here.</i>
</li>
<li>
<label><input class="inlineCheckers" type="checkbox" id="checkers4" onchange="setCheck4();" /><b id="grayTitle4">List Title 4</b></label>
<i id="hiddenDiv4">Div content can go here.</i>
</li>
</ol>
Here is my current JS:
function setCheck() {
var el = document.getElementById("checkers");
if (el.checked) {
document.getElementById("hiddenDiv").className = "main";
document.getElementById("grayTitle").className = "titleGray";
} else {
document.getElementById("hiddenDiv").className = "";
document.getElementById("grayTitle").className = "";
}
}
function setCheck2() {
var el2 = document.getElementById("checkers2");
if (el2.checked) {
document.getElementById("hiddenDiv2").className = "main";
document.getElementById("grayTitle2").className = "titleGray";
} else {
document.getElementById("hiddenDiv2").className = "";
document.getElementById("grayTitle2").className = "";
}
}
function setCheck3() {
var el3 = document.getElementById("checkers3");
if (el3.checked) {
document.getElementById("hiddenDiv3").className = "main";
document.getElementById("grayTitle3").className = "titleGray";
} else {
document.getElementById("hiddenDiv3").className = "";
document.getElementById("grayTitle3").className = "";
}
}
function setCheck4() {
var el4 = document.getElementById("checkers4");
if (el4.checked) {
document.getElementById("hiddenDiv4").className = "main";
document.getElementById("grayTitle4").className = "titleGray";
} else {
document.getElementById("hiddenDiv4").className = "";
document.getElementById("grayTitle4").className = "";
}
}
I realize I don't have the AJAX portion of this setup yet, but I wanted to make that requirement known, as I will be adding that part into the code shortly. Thanks for any guidance you can provide!
This is how I would do it:
$toolkitlist.on( 'change', '.inlineCheckers', function () {
$( this ).closest( 'li' ).toggleClass( 'checked', this.checked );
});
where $toolkitlist is a jQuery object containing the OL element. Notice how I set the class on the LI element, because that is the outermost element which represents the checked item. You can use these selectors to style the "checked" states:
/* how they look like initially */
.grayTitle { ... }
.hiddenDiv { ... }
/* how they look like when their check-box is checked */
li.checked .grayTitle { ... }
li.checked .hiddenDiv { ... }
use jQuery's addClass
$('#grayTitle3').addClass('titleGray');
Update, using ^=:
$('[id^=grayTitle]').addClass('titleGray');
You could also apply the class to outer div
CSS:
.selected label b {
color:blue
}
.selected i {
color:red
}
JS:
$(".inlineCheckers").on('click',function(){
$(this).parents("li").toggleClass('selected',$(this).is(":checked"));
})
jsfiddle : http://jsfiddle.net/y2rHF/3/
remove the inline .onchange.. and bind the change event to your class .inlineCheckers - then you can usethis` so you don't have to make multiple functions for each set
$('.inlineCheckers').on('change', function {
$el = $(this);
var $hd = $el.closest('label').next('i');
var $gt = $el.next('b');
$hd.toggleClass('main', $el[0].checked);
$gt.toggleClass('checkers', $el[0].checked);
}
http://jsfiddle.net/KYzgB/