I can't implement the check function if (element.classList.contains(__active)). That is, if the element already contains the class "active", then you need to remove this class from the element when you click on it.
Codepen
You can remove without checking. Try this:
const clickHandler = () => {
document.querySelector('.active')?.classList.remove('active');
};
div {
color: red;
background: black;
margin-bottom: 16px
}
div.active {
color: black;
background: red;
}
<div class="active">CHNAGE ME</div>
<button onClick="clickHandler()">CLICK TO CHANGE</button>
Related
I have several identical divs and each of them contains a button that is hidden. I want to make button visible when you hover on the parent div. I wrote this code:
const cardElements = document.querySelectorAll('.middle_section__president_section');
const learnButtons = document.querySelectorAll('.president_section__button');
cardElements.forEach((cardElement) => {
cardElement.addEventListener('mouseover', () => {
learnButtons.forEach((learnButton) => {
learnButton.style.height = "50px";
learnButton.style.opacity = "1";
learnButton.style.border = "3px solid rgb(129, 129, 129)";
});
});
cardElement.addEventListener('mouseout', () => {
learnButtons.forEach((learnButton) => {
learnButton.style.height = "0px";
learnButton.style.opacity = "0";
learnButton.style.border = "0px solid rgb(129, 129, 129)";
});
});
})
carElements is parent, learnButtons - child.
but with this code when i hover on one div buttons appears in every similiar div. How can i make button appear only on hovered div?
Use the Event object
cardElement.addEventListener('mouseover', () => {
learnButtons.forEach((learnButton) => {
convert this to
cardElement.addEventListener('mouseover', (e) => {
var learnButton = e.target;
There's no need to use JS for this. As Mister Jojo/traktor pointed out in their comments you can use the CSS :hover pseudo-class instead.
The key CSS line is .box:hover button { visibility: visible;} which means "when you hover over the parent container make its button visible".
.box { width: 50%; display: flex; flex-direction: column; border: 1px solid lightgray; margin: 0.25em; padding: 0.25em;}
button { visibility: hidden; margin: 0.25em 0; border-radius: 5px; background-color: lightgreen; }
.box:hover button { visibility: visible;}
.box:hover, button:hover { cursor: pointer; }
<section class="box">
Some text
<button>Click for a surprise!</button>
</section>
<section class="box">
Some text
<button>Click for a surprise!</button>
</section>
<section class="box">
Some text
<button>Click for a surprise!</button>
</section>
It is bad practice to iterate over all elements and give each an event, as you can add 1 event handler to the parent and when the event happens you can check the affected element by the event parameter in the handler call back
parent.addEVentListener('mouseover', (e) => {
if(e.target.classList.contains('middle_section__president_section')) {
// Do
}
});
I just started school, and this is my first question ever asked on Stackoverflow, so I apologize up front regarding both formatting and wording of this question.
I want to change the border color of my div to a style I have already declared when I click on it. To show that this has been selected.
I have three divs with id="red/green/pink".
Now, is there a way to change this function to grab information from the div I clicked, so I dont have to write 3 (almost) identical functions?
.chosenBorder{
border: 3px solid gold;
}
<div id="red" class="mainDivs" onclick="newColor('red')">Red?</div>
<div id="green" class="mainDivs" onclick="newColor('green')">Green?</div>
<div id="pink" class="mainDivs" onclick="newColor('pink')">Pink?</div>
<div class="mainDivs" onclick="whatNow(changeBig)">Choose!</div>
<script>
let changeBig = "";
let chosenDiv = document.getElementById("body");
function newColor(thisColor) {
changeBig = thisColor;
// something that make this part dynamic.classList.toggle("chosenBorder");
}
function whatNow(changeBig) {
document.body.style.backgroundColor = changeBig;
}
</script>
Since you already have an id contains the name of color; get the advantage of it: and keep track of the selected color in your variable changeBig.
let changeBig = "";
function newColor(div) {
// initial all divs to black
initialDivs();
div.style.borderColor = div.id;
changeBig = div.id;
}
function initialDivs() {
[...document.querySelectorAll('.mainDivs')].forEach(div => {
div.style.borderColor = 'black'
});
}
function whatNow() {
document.body.style.backgroundColor = changeBig;
}
.mainDivs {
padding: 10px;
margin: 10px;
border: 3px solid;
outline: 3px solid;
width: fit-content;
cursor: pointer;
}
<div id="red" class="mainDivs" onclick="newColor(this)">Red?</div>
<div id="green" class="mainDivs" onclick="newColor(this)">Green?</div>
<div id="pink" class="mainDivs" onclick="newColor(this)">Pink?</div>
<div class="mainDivs" onclick="whatNow()">Choose!</div>
There are a few (modern) modifications you can make to simplify things.
Remove the inline JS.
Use CSS to store the style information.
Use data attributes to store the colour rather than the id.
Wrap the div elements (I've called them boxes here) in a containing element. This way you can use a technique called event delegation. By attaching one listener to the container you can have that listen to events from its child elements as they "bubble up" the DOM. When an event is caught it calls a function that 1) checks that the event is from a box element 2) retrieves the color from the element's dataset, and adds it to its classList along with an active class.
// Cache the elements
const boxes = document.querySelectorAll('.box');
const container = document.querySelector('.boxes');
const button = document.querySelector('button');
// Add a listener to the container which calls
// `handleClick` when it catches an event fired from one of
// its child elements, and a listener to the button to change
// the background
container.addEventListener('click', handleClick);
button.addEventListener('click', handleBackground);
function handleClick(e) {
// Check to see if the child element that fired
// the event has a box class
if (e.target.matches('.box')) {
// Remove the color and active classes from
// all the boxes
boxes.forEach(box => box.className = 'box');
// Destructure the color from its dataset, and
// add that to the class list of the clicked box
// along with an active class
const { color } = e.target.dataset;
e.target.classList.add(color, 'active');
}
}
function handleBackground() {
// Get the active box, get its color, and then assign
// that color to the body background
const active = document.querySelector('.box.active');
const { color } = active.dataset;
document.body.style.backgroundColor = color;
}
.boxes { display: flex; flex-direction: row; background-color: white; padding: 0.4em;}
.box { display: flex; justify-content: center; align-items: center; width: 50px; height: 50px; border: 2px solid #dfdfdf; margin-right: 0.25em; }
button { margin-top: 1em; }
button:hover { cursor: pointer; }
.box:hover { cursor: pointer; }
.red { border: 2px solid red; }
.green { border: 2px solid green; }
.pink { border: 2px solid pink; }
<div class="boxes">
<div class="box" data-color="red">Red</div>
<div class="box" data-color="green">Green</div>
<div class="box" data-color="pink">Pink</div>
</div>
<button>Change background</button>
This is the markup that I'm working with
<div class="checkout-row__form-column__billing">
<div class="checkout-row__form-column__billing__title billing-collapse collapse-btn">
<div class="checkout-row__form-column__billing__title__arrow">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</div>
<h2>1. Billing Details</h2>
</div>
<div class="checkout-row__form-column__shipping__content shipping-collapse-content collapse-target"></div>
<div class="checkout-row__form-column__shipping">
<div class="checkout-row__form-column__shipping__title shipping-collapse collapse-btn">
<div class="checkout-row__form-column__shipping__title__arrow">
<i class="fa fa-chevron-right" aria-hidden="true"></i>
</div>
<h2>2. Shipping Details</h2>
</div>
<div class="checkout-row__form-column__shipping__content shipping-collapse-content collapse-target"></div>
All this markup is wrapped in div with a class of checkout-row__form-column.
So, I'm grabbing the parent wrapper, on click I match target with .collapse-btn. and then I do class toggle on the sibling which is .collapse-target. Works great except on issue, that I can't get past. If I click on the inner element, the event doesn't happen.
Here is my js
parent = document.querySelector('.checkout-row__form-column');
parent.addEventListener('click', (e)=>{
if (e.target.matches('.collapse-btn')) {
e.stopPropagation();
e.target.nextElementSibling.classList.toggle('hide');
}
});
With these code I am able to toggle classes if I don't click on i element or h2. How can I write my JavaScript that event will be fired if I click anywhere inside the e.target.
Any help is much appreciated fellow commarades.
Your code only checks if the event.target is the .collapse-btn element or not. You also need to check if the clicked element is a descendant element of the .collapse-btn element.
To achieve the desired result, you can use the two conditions to check if the event.target is the .collapse-btn or any of its descendant. To check for descendant elements, use the universal selector *.
parent.addEventListener('click', (e) => {
if (
e.target.matches('.collapse-btn') ||
e.target.matches('.collapse-btn *')
) {
// code
}
});
Demo
Following snippet shows an example:
const parent = document.querySelector('.container');
parent.addEventListener('click', e => {
if (
e.target.matches('.collapse-btn') ||
e.target.matches('.collapse-btn *')
) {
parent.classList.toggle('active');
}
});
.container {
background: #eee;
width: 120px;
height: 120px;
}
.container.active {
border: 2px solid;
}
.collapse-btn {
background: #999;
padding: 10px;
}
.collapse-btn span {
background: #fc3;
font-size: 1.3rem;
cursor: pointer;
}
<div class="container">
<div class="collapse-btn">
<span>Click</span>
</div>
</div>
You could also put the selectors in an array and then use .some() method to check if event.target matches ay of the selector in the array.
const selectors = ['.collapse-btn', '.collapse-btn *'];
parent.addEventListener('click', e => {
if (selectors.some(s => e.target.matches(s))) {
// code
}
});
Demo
Following snippet shows an example:
const parent = document.querySelector('.container');
parent.addEventListener('click', e => {
const selectors = ['.collapse-btn', '.collapse-btn *'];
if (selectors.some(s => e.target.matches(s))) {
parent.classList.toggle('active');
}
});
.container {
background: #eee;
width: 120px;
height: 120px;
}
.container.active {
border: 2px solid;
}
.collapse-btn {
background: #999;
padding: 10px;
}
.collapse-btn span {
background: #fc3;
font-size: 1.3rem;
cursor: pointer;
}
<div class="container">
<div class="collapse-btn">
<span>Click</span>
</div>
</div>
Alternative Solution
Instead of using the .matches() method, use the combination of Element.closest() and Node.contains() methods to check if the event.target is the .collapse-btn element or is a child element of the .collapse-btn element.
For this to work, you need to do two steps:
You first need to get the .collapse-btn that is the closest ancestor of the event.target.
After that, you need to check if the event.target is a descendant of the button selected in step 1.
Code:
parent.addEventListener('click', (e) => {
const targetCollapseBtn = e.target.closest('.collapse-btn');
if (targetCollapseBtn && targetCollapseBtn.contains(e.target)) {
...
}
});
If event.target is the .collapse-btn itself, then
e.target.closest('.collpase-btn')
will return the e.target, i.e. .collapse-btn and
targetCollapseBtn.contains(e.target)
will also return true because .contains() method returns true even if the node passed to it as an argument is the same node as the one on which .contains() method was called.
So using .closest() and .contains() methods in this way covers both the use cases, i.e. when .collapse-btn is clicked or when any of its descendant element is clicked.
Demo
Following snippet shows a simple example:
const parent = document.querySelector('.container');
parent.addEventListener('click', e => {
const targetCollapseBtn = e.target.closest('.collapse-btn');
if (targetCollapseBtn && targetCollapseBtn.contains(e.target)) {
parent.classList.toggle('active');
}
});
.container {
background: #eee;
width: 120px;
height: 120px;
}
.container.active {
border: 2px solid;
}
.collapse-btn {
background: #999;
padding: 10px;
}
.collapse-btn span {
background: #fc3;
font-size: 1.3rem;
cursor: pointer;
}
<div class="container">
<div class="collapse-btn">
<span>Click</span>
</div>
</div>
I am trying to write a code that toggles the styling of a button. At first, I thought I could do this with CSS and the active selector. But I also want to be able to undo the changes on click, which the active selector does not. Then I thought I might be able to do this with JavaScript and a class toggle function.
Unfortunately, I am not that familiar with JavaScript. I have written a code and it partially works, but it always changes the styling of the same element (maybe because it is the first element with the specific class?).
Is there a way to build a flexible function which can apply to several elements, depending on which one I click on?
function testtoggle() {
if(document.getElementById("testknop").className == "nietactief"){
document.getElementById("testknop").className = "actief";
} else {
document.getElementById("testknop").className = "nietactief";
}
}
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()()">1e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()">2e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle()()">3e</button>
</body>
</html>
You should look into the classList feature in JavaScript. It makes it easy to add, remove and toggle classes on elements.
// Get a list of all the buttons in the group
let buttons = Array.from(document.querySelectorAll('[type=button]'))
// For each button add a click event listener
// This allows us to remove the 'onclick' from the html
buttons.forEach(item => {
item.addEventListener('click', e => {
// When the button is clicked, remove the active state
// Then add the inactive state
buttons.forEach(button => {
// If the current button does not equal the clicked button
// Remove the active state class
button != e.currentTarget && button.classList.remove("actief")
button.classList.add("nietactief")
})
// Toggle the state of the clicked button
item.classList.toggle("actief")
})
})
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<button type="button" id="testknop" class="nietactief">1e</button>
<button type="button" id="testknop" class="nietactief">2e</button>
<button type="button" id="testknop" class="nietactief">3e</button>
</body>
</html>
What you are observing is the expected behavior to the document.getElementById
You could pass a reference to the button triggering the event:
window.testtoggle = function(elmnt) {
if(elmnt.className == "nietactief"){
elmnt.className = "actief";
} else {
elmnt.className = "nietactief";
}
}
button {
cursor: pointer;
padding: 16px;
font-size: 16px;
border-radius: 100%;
}
.nietactief {
background-color: #a8a8a8;
border: 5px solid #ddd;
}
.actief {
background-color: purple;
border: 5px solid green;
}
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">1e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">2e</button>
<button type="button" id="testknop" class="nietactief" onclick="testtoggle(this)">3e</button>
Alternatively you could consider JQuery to fetch all dom elements matching a specific criteria and update its class:
$("button").attr('class', 'newClass');
or another client framework like vue.js
I want to change the background color of the button based upon the class. Why it is not going back after second click?
var $begin1 = $(".begin1").click(function(e) {
e.preventDefault();
var buttonState = $(this).attr("class");
if (buttonState != 'pressed') {
$begin1.removeClass('pressed');
$(this).addClass('pressed');
} else {
$(this).removeClass('pressed');
$(this).addClass('unpressed');
}
});
li {
list-style-type: none;
}
.begin1.unpressed,
.begin2.unpressed {
background-color: white;
color: black;
border: 2px solid #4CAF50;
margin: 0 0 10px 0;
}
li.begin1.pressed,
li.begin2.pressed {
background: #4CAF50;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<li class="begin1 unpressed">
<h2>Button</h2>
</li>
https://jsfiddle.net/nrebaL00/
You can simplify your code greatly. Apply the default styling from the beginning and you don't need an .unpressed class.
The issue with using .attr( 'class' ) is that it will retrieve all the classes applied to the element as a string. Performing a check like if ( 'a' === $el.attr( 'class' ) ) won't work where $el is <li class="a b c"> as $el.attr( 'class' ) would return 'a b c' and not 'a'. Which is why your check was failing after the first click. This kind of check would be good for .hasClass().
e.prevendDefault() is not required for an <li>, so remove that.
Note: the selector I used for jQuery is pretty generic. You may need to increase it's specificity if there are other <li> on the page that don't require the functionality. Something along the lines of adding a class to the <ul> and using that as the part of the jQuery selector. i.e. <ul class="clicky-mcclickens"> and $( '.clicky-mcclickens li' ).
$('li').on('click', function(e) {
$(this).toggleClass('pressed');
});
li {
list-style-type: none;
background-color: white;
color: black;
border: 2px solid #4CAF50;
margin: 0 0 10px 0;
}
.pressed {
background: #4CAF50;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<h2>Button 1</h2>
</li>
<li>
<h2>Button 2</h2>
</li>
</ul>
Sometimes you need more control than simply adding/removing a class when an element is clicked. In those instances you can use .hasClass() to check if the element has the class in question and apply the appropriate action.
Your code is much more complex than it needs to be; you can just call toggleClass() like this:
var $begin1 = $(".begin1").click(function() {
$(this).toggleClass('pressed unpressed');
});
Updated fiddle
Note that e.preventDefault() is redundant for an li element as it has no default behaviour to prevent.
I would use toggleClass instead of adding and removing manually. This seems to work:
var $begin1 = $(".begin1").click( function(e) {
$begin1.toggleClass('pressed');
});
Instead of check the complete string of the class of the element you can check if the element has specific class using hasClass:
var $begin1 = $(".begin1").click( function(e) {
e.preventDefault();
if(!$(this).hasClass('pressed')){
$begin1.removeClass('unpressed');
$(this).addClass('pressed');
} else{
$(this).removeClass('pressed');
$(this).addClass('unpressed');
}
});
li{
list-style-type: none;
}
.begin1.unpressed,
.begin2.unpressed {
background-color: white;
color: black;
border: 2px solid #4CAF50;
margin: 0 0 10px 0;
}
li.begin1.pressed,
li.begin2.pressed{
background: #4CAF50;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<li class="begin1 unpressed"><h2>Button</h2></li>
The problem with using the attr('class') is that you can't know what exactly will be the final string.
Just replace your js with:
$(document).ready(function() {
$(".begin1").click(function(){
$(this).toggleClass("pressed");
});
});