Remove same class from several element in javascript - javascript

I have 4 Button with class of active and now I wanna remove all of those class and then only add 1 class to the one I clicked on.
<button class="active btn">btn1</button>
<button class="active btn">btn2</button>
<button class="active btn">btn3</button>
<button class="active btn">btn4</button>
here is code I wrote so far:
let buttonOptions = document.querySelectorAll(".btn");
for (let i=0; i< buttonOptions.length; i++) {
buttonOptions.addEventListener('click', function() {
buttonOptions[i].classList.add("active");
});
}
but I don't know how should I remove those active class.
and also I wanna know there is a way to avoid using for specifically for removing class?

var buttons = document.getElementsByClassName("btn");
function removeAllActive() {
[].forEach.call(buttons, function(el) {
el.classList.remove("active");
});
}
removeAllActive();
[].forEach.call(buttons, function(el) {
el.addEventListener("click", function() {
removeAllActive();
el.classList.add("active");
});
});
.active {
background-color: red;
}
<button class="active btn">btn1</button>
<button class="active btn">btn2</button>
<button class="active btn">btn3</button>
<button class="active btn">btn4</button>

You could avoid using a for loop, but you'd still have to iterate, so there's really no point. I'll use forEach as it's supported on querySelector and looks a little cleaner, but a for loop is fine as well
Same goes for binding the event handler, you have to iterate and target each element
let buttonOptions = document.querySelectorAll(".btn");
buttonOptions.forEach( el =>
el.addEventListener('click', function() {
buttonOptions.forEach( els => els.classList.remove('active') )
this.classList.add("active");
})
)
.active {color : red}
<button class="active btn">btn1</button>
<button class="btn">btn2</button>
<button class="btn">btn3</button>
<button class="btn">btn4</button>

document.getElementById('buttons').addEventListener('click', function(evt) {
if (evt.target.classList.contains('btn')) {
Array.from(document.querySelectorAll('.btn', this)).forEach(x => {
x.classList.toggle('active', x == evt.target)
});
}
});
.active {
color: red;
}
<div id="buttons">
<button class="active btn">btn1</button>
<button class="btn">btn2</button>
<button class="btn">btn3</button>
<button class="btn">btn4</button>
</div>
Attaching a listener to each button is wasteful. I attach a listener to a containing element, then wait for the event to bubble up to it. If the click originated from one of the btn-classed elements, then I find all the btn-classed elements within it and switch their active class based on whether they are the clicked button or not.

Here is the easiest way to resolve the mentioned issue,
$(function){
$(document).on("click",".btn",function(){
$(".btn").removeClass("active");
$(this).addClass("active");
});
});

If you are allowed to use jquery, it will be much easier for you. Please have a look on the below code,it may help you
<script>
$( document ).ready(function() {
$('.btn').removeClass('active');
$('.btn').click(function(e) {
$('.btn').removeClass('active');
$(this).addClass('active');
});
});
</script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<button class="active btn">btn1</button>
<button class="active btn">btn2</button>
<button class="active btn">btn3</button>
<button class="active btn">btn4</button>

Try this one:
$(document).on("click",".btn",function(){
$(".btn").removeClass("active");
$(this).addClass("active");
});

Related

Only one and first div is triggered when click

I wanted to log the elements of the clicked button to the console, but it only worked for "CLICK ME 1." It did not work for another button.
var testLog = (function(){
var getButton = document.querySelector('.button__');
getButton.addEventListener('click', checkPlease);
function checkPlease(event){
console.log(event.target);
}
})();
<section class="product">
<div class="product__wrapper">
<div class="item">
<button class="button__">CLICK ME 1</button>
<button class="button__">CLICK ME 2</button>
</div>
</div>
</section>
Please use querySelectorAll to get all the elements and then forEach on the returned nodelist to attach addEventListener to each element
var getButtonList = document.querySelectorAll('.button__');
getButtonList.forEach(el => el.addEventListener('click', checkPlease));
you get only first button, because of document.querySelector in your code. Use document.querySelectorAll instead
Attach your event listener to all elements, using loop, like this:
var getButton = document.querySelectorAll('.button__');
getButton.forEach(el => el.addEventListener('click', checkPlease))
Try this: you need to use querySelectorAll:
var getButton = document.querySelectorAll(".button__");
if (getButton && getButton.length > 0) {
getButton.forEach((element) => {
element.addEventListener("click", checkPlease);
});
}
function checkPlease(event: any) {
console.log(event.target);
}
Instead of adding a listener per button, you can use event delegation by adding a single listener to the parent element and differentiating by target details.
const testLog = (function(){
const buttonContainer = document.querySelector('.item');
buttonContainer.addEventListener('click', checkPlease);
function checkPlease(event){
console.log(event.target);
}
})();
<section class="product">
<div class="product__wrapper">
<div class="item">
<button class="button__">CLICK ME 1</button>
<button class="button__">CLICK ME 2</button>
</div>
</div>
</section>

querySelectorAll not working with Cloned item

I'm trying to make a copy of the original container div with the cloneNode method in javascript inside the container there are 3 buttons with btn class, when I make a copy of the original one the last only the last element in the copied item is only printing hello in the console, any ideas?
let add = document.querySelector('.add-button');
const item = document.querySelector('.container');
let btn = document.querySelectorAll('.btn');
add.addEventListener('click', function() {
makecopy();
});
btn.forEach(el => {
el.addEventListener('click', function() {
console.log("hello")
})
});
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
}
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
You are only setting up event listeners on the first set of buttons, not the cloned ones. Instead of setting up listeners on each button, use "event delegation" to allow the event to "bubble" up to a common ancestor and handle the event there. This way, all the newly added elements will immediately work without needing their own handler and there is only one handler that needs to be set up instead of many.
You've also got some redundant code and code that will no longer be needed when you take this approach.
// No need to set up an anonymous handler that calls the real one. Just
// register the real one
document.querySelector('.add-button').addEventListener('click', makecopy);
const item = document.querySelector('.container');
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
}
// Listen for clicks on the document:
document.addEventListener('click', function(event) {
// Check to see if it was a button that was clicked:
if(event.target.classList.contains("btn")){
console.log("hello");
};
});
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
</div>
Just appears you weren't adding new listeners when you made new buttons. Some adjustments.
let add = document.querySelector('.add-button');
const item = document.querySelector('.container');
let btn = document.querySelectorAll('.btn');
add.addEventListener('click', function() {
makecopy();
});
btn.forEach(el => {
el.addEventListener('click', function() {
console.log("hello")
})
});
function makecopy() {
let copiedItem = item.cloneNode(true);
item.parentNode.insertBefore(copiedItem, item);
copiedItem.addEventListener('click', function() {
console.log("hello")
})
}
<div class="add-panel">
<button type="button" class="add-button">Create new</button>
</div>
<div class="container">
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>
<div>
<button type="button" class="btn">+</button>
</div>

Different values for innerHTML in debug and console log

I'm encountering a typical situation while accessing the innerHTML property using jQuery. I've fetched the target button using the jQuery attribute selector.
Below is the snippet of jQuery attribute selector.
jQuery('button[type="button"][class="btn btn-primary"]').each(function () {
var btn = jQuery(this);
console.log(btn);
if (btn[0].innerHTML === "OK") {
console.log("ok");
jQuery(this).click();
}
});
Following is the screenshot of the console log of the target button. It's innerHTML property is set to OK.
Following is the screenshot of the value of the innerHTML while debugging the target button object. In this case the value is "".
Ideally, the values of the innerHTML should be the same for both the cases.
EDIT
Why does this behavior differ that the ideal one? For both of the cases, the value of the innerHTML should be the same.
Also, there were multiple buttons. I have taken screenshots of different buttons. Thus their ID's are different. But still, the behavior is same.
Try something like this.
function SomeEvent($ele) {
alert($ele.html());
return false;
}
function invokeBtnEvents() {
$("[data-action=some-event]").off(); // clear old events
$("[data-action=some-event]").on("click", function() {
var $this = $(this);
return SomeEvent($this);
}) // define event(s)
return false;
}
$(document).ready(function() {
invokeBtnEvents();
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event" class="btn btn-primary">Button 1</button>
<button data-action="some-event" class="btn btn-primary">Button 2</button>
<button data-action="some-event" class="btn btn-primary">Button 3</button>
<button data-action="some-event" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>
Your issue
What you are doing that I think is your main issue is the $.each() method.
Store the buttons in a variable,
let $button = $("button"); // this returns an array of all the buttons on the DOM
You can then use the $.each() method to get the clicked element
$.each($button, function(index, btn){
const $this = $(btn);
$this.off();
$this.click(function(){someEvent($this)});
});
I would not invoke button clicks like this because every time you click a button, this each loop gets ran. It will then send all of the buttons to that action unless you parse by an ID or something (you are using the innerText).
If you use the code in my snippet, only the clicked button will be triggered.
An alternative approach to my first snippet is using something like a dispatcher.
function DoActionOneClick($ele){
alert("Action 1 " + $ele.html());
}
function DoDefaultClick($ele){
alert("Default Action " + $ele.html());
}
function DispatchEvent(action, $ele){
switch(action){
case "some-event-1":
DoActionOneClick($ele);
break;
default:
DoDefaultClick($ele);
break;
}
}
function invokeActions(){
$("[data-action]").off();
$("[data-action]").on("click", function(){
// get this
var $this = $(this);
// get action
var action = $this.data().action;
DispatchEvent(action, $this);
});
}
$(document).ready(function(){
invokeActions();
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event-1" class="btn btn-primary">Button 1</button>
<button data-action="some-event-2" class="btn btn-primary">Button 2</button>
<button data-action="some-event-2" class="btn btn-primary">Button 3</button>
<button data-action="some-event-2" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>

How can I get a substring of an ID name?

I have a number of buttons within a section, each with an id of the form #balls-left-n, where n ranges from 1 to 15.
When one of these buttons is clicked, I want to grab the number from the id that was clicked and hide all of the buttons with ids that have names including numbers that are greater than the one clicked on.
So, if #balls-left-13 is clicked, I want to hide #balls-left-14 and #balls-left-15. But if #balls-left-3 is clicked I want to hide all the buttons from #balls-left-4 through #balls-left-15.
I'm a novice at web-dev so if I've made other mistakes or taken a poor approach don't hesitate to point that out.
I have a handler for each of the buttons (which if I knew more could probably be one function) that look like this:
$("#balls-left-14").click(function() {
var num_balls = $(this).attr('id').match(/[\d]/);
j_end_balls_on_table = 14;
$("#balls-left button:gt(num_balls-2)").hide;
...
other stuff
...
});
This didn't work and I get an error that num_balls is undefined, which I don't understand.
#balls-left is the section all of the buttons are inside of.
relevant HTML as requested
<section id="balls-left">
<h2>How Many Balls are Left on the Table?</h2>
<button type="button" id="balls-left-2" class="x-balls-left">2</button>
<button type="button" id="balls-left-3" class="x-balls-left">3</button>
<button type="button" id="balls-left-4" class="x-balls-left">4</button>
<button type="button" id="balls-left-5" class="x-balls-left">5</button>
<button type="button" id="balls-left-6" class="x-balls-left">6</button>
<button type="button" id="balls-left-7" class="x-balls-left">7</button>
<button type="button" id="balls-left-8" class="x-balls-left">8</button>
<button type="button" id="balls-left-9" class="x-balls-left">9</button>
<button type="button" id="balls-left-10" class="x-balls-left">10</button>
<button type="button" id="balls-left-11" class="x-balls-left">11</button>
<button type="button" id="balls-left-12" class="x-balls-left">12</button>
<button type="button" id="balls-left-13" class="x-balls-left">13</button>
<button type="button" id="balls-left-14" class="x-balls-left">14</button>
<button type="button" id="balls-left-15" class="x-balls-left">15</button>
</section>
Hope this helps.
var exploded = id.split("-");
alert(exploded.pop());
Now, to use that concept on your HTML structure, you can do something like this:
$(document).ready(function() {
$(".x-balls-left").click(function(e) {
e.preventDefault();
var exploded = this.id.split("-");
alert(exploded.pop());
});
});
And here's a Fiddle you can play around with.
You might don't even need all of these if your elements to hide share the same parent. Just set class on click .selected and hide the rest using CSS .selected.x-balls-left ~ .x-balls-left {display: none;}
$('.x-balls-left').click(function() {
$(this).toggleClass('selected');
})
.selected.x-balls-left ~ .x-balls-left {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="balls-left">
<h2>How Many Balls are Left on the Table?</h2>
<button type="button" id="balls-left-2" class="x-balls-left">2</button>
<button type="button" id="balls-left-3" class="x-balls-left">3</button>
<button type="button" id="balls-left-4" class="x-balls-left">4</button>
<button type="button" id="balls-left-5" class="x-balls-left">5</button>
<button type="button" id="balls-left-6" class="x-balls-left">6</button>
<button type="button" id="balls-left-7" class="x-balls-left">7</button>
<button type="button" id="balls-left-8" class="x-balls-left">8</button>
<button type="button" id="balls-left-9" class="x-balls-left">9</button>
<button type="button" id="balls-left-10" class="x-balls-left">10</button>
<button type="button" id="balls-left-11" class="x-balls-left">11</button>
<button type="button" id="balls-left-12" class="x-balls-left">12</button>
<button type="button" id="balls-left-13" class="x-balls-left">13</button>
<button type="button" id="balls-left-14" class="x-balls-left">14</button>
<button type="button" id="balls-left-15" class="x-balls-left">15</button>
</section>
$(document).on('click', '.balls-left', function() {
var num = getNum(this);
$('.balls-left').each(function() {
var that = $(this);
var bnum = getNum(that);
if (bnum > num) {
that.show();
} else {
that.hide();
}
});
});
var getNum = function(elem) {
if (elem) {
return $(elem).attr('id').replace('balls-left-', '');
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="balls-left-1" class="balls-left">Ball 1</div>
<div id="balls-left-2" class="balls-left">Ball 2</div>
<div id="balls-left-3" class="balls-left">Ball 3</div>
<div id="balls-left-4" class="balls-left">Ball 4</div>
<div id="balls-left-5" class="balls-left">Ball 5</div>
$("#balls-left button:gt(num_balls-2)").hide;
This is an invalid CSS selector, and only gets the hide method, without calling it. You want something like:
$("#balls-left button:gt("+(num_balls-2)+")").hide();
First you should put a class on each object so you can reference them all at once, and the simplest way to understand is to just put the ball number right in the tag as a custom attribute if you can:
<input type="button" id="balls-left-1" class="left-ball" num="1"/>
<input type="button" id="balls-left-2" class="left-ball" num="2"/>
etc...
Then you can write the javascript as follows:
$('.left-ball').click(function () {
var BallNum = $(this).attr('num');
$('.left-ball').each(function () {
if ($(this).attr('num') > BallNum) {
$(this).hide();
}
});
});
You can use RegEx match like this. This might resolve your undefined num_balls error message.
$("#balls-left-14").click(function() {
var ret = $(this).attr('id').match("[0-9]+");
var num_balls = ret[0];
j_end_balls_on_table = 14;
$("#balls-left button:gt(num_balls-2)").hide;
...
other stuff
...
});
Another way of doing it using your original HTML:
$('.x-balls-left').click(function () {
var BallNum = $(this)[0].innerHTML;
$('.x-balls-left').each(function () {
if ($(this)[0].innerHTML > BallNum) {
$(this).hide();
}
});
});
I just did it like this:
$('button[id^=balls-left-]').click(function(){
var num_balls = $(this).attr('id').match(/[\d]/);
$('#balls-left button:gt(' + num_balls + ')').hide();
});
Keep in mind that :gt select by index, it means that $('#balls-left button:gt(2)') will not select the button with id balls-left-2 but the one with id balls-left-4 (according to the html you posted).

how to stop repetition in jquery code?

I'm trying to make a image library and my code contains much repetition. Does anyone of you have an idea on how to make this work better?
Here is a exemple of the code
HTML
<div id="wrapper">
<div id="buttoncont">
<button id="all" type="button">All</button>
<button id="dog" type="button">Dog</button>
<button id="cat" type="button">Cat</button>
</div>
<img class="cat_img" src="http://upload.wikimedia.org/wikipedia/commons/2/22/Turkish_Van_Cat.jpg" />
<img class="dog_img" src="http://upload.wikimedia.org/wikipedia/commons/2/26/YellowLabradorLooking_new.jpg" />
</div>
JS
$("#cat").click(function () {
$("img.dog_img").hide();
$("img.cat_img").show();
});
$("#dog").click(function () {
$("img.cat_img").hide();
$("img.dog_img").show();
});
$("#all").click(function () {
$(".cat_img, .dog_img").show();
});
First: you need to set a default selected to make the Toggle work as expected.
Use CSS Classes to make the divs into one group(class) and bind the event to it, see the HTML:
<div id="wrapper">
<div id="buttoncont">
<button id="all" type="button">All</button>
<button class=toggle id="dog" type="button">Dog</button>
<button class=toggle id="cat" type="button">Cat</button>
</div>
<img class="cat_img" src="http://upload.wikimedia.org/wikipedia/commons/2/22/Turkish_Van_Cat.jpg" />
<img class="dog_img" src="http://upload.wikimedia.org/wikipedia/commons/2/26/YellowLabradorLooking_new.jpg" />
</div>
And on the Javascript Code, do a function to do it for you and use jQuery Toggle:
function toggleImg(){
$('img.dog_img').toggle();
$('img.cat_img').toggle();
}
$(".toggle").click(function () {
toggleImg();
});
$("#all").click(function () {
$(".cat_img, .dog_img").show();
});
But on the onLoad event of your page Toggle one of they indivually to make it the default selected, this way Toggling the two, always only one will be visible.
$('html').ready(function(){
$('img.cat_img').toggle();
});
function toggleImages(e){
$('img').hide();
$('img.'+e.target.id+'_img').show();
}
$("#cat").click(toggleImages);
$("#dog").click(toggleImages);
$("#all").click(function () {
$(".cat_img, .dog_img").show();
});
<div id="wrapper">
<div id="buttoncont">
<button id="all" type="button">All</button>
<button id="dog" type="button">Dog</button>
<button id="cat" type="button">Cat</button>
</div>
<img class="cat_img" src="http://upload.wikimedia.org/wikipedia/commons/2/22/Turkish_Van_Cat.jpg" />
<img class="dog_img" src="http://upload.wikimedia.org/wikipedia/commons/2/26/YellowLabradorLooking_new.jpg" />
</div>
I would do it like this
Keep the html as you have it.
$(".toggle").click(
function() {
$("."+ $(this).attr('id') + "_img").toggle()
}
);
$("#all").click(
function() {
$("img[class$='_img']").show()
}
);
This way you can add as many images and buttons as you want without modifying your javascript
Here is a jsFiddle
You can use this :
$('#buttoncont button').click(function(){
if(this.id=='all'){
$('img').show();
}else{
$('img').hide().filter('.'+ this.id +'_img').show();
}
})
Fiddle : http://jsfiddle.net/b65EL/
An idea
$("#all").click(function(){
$(".cat_img, .dog_img").show();
}
$("#dog, #cat").click(function(){
$("img."+this.id+"_img").toggle();
}
Remove the ID attributes on the buttons unless you need them for something else.
Add a custom attribute to the buttons, such as:
<button id="cat" type="button" image-clicker="cat_img">Cat</button>
Then add this jquery to the page (I didn't test this so you may have to fix minor issues with it):
$(function(){
$("[image-clicker]").each(function(){
var buttonClicked = $(this);
var targetID = buttonClicked.attr("image-clicker");
var targetElement = $("#"+targetID);
$(this).click(function(){
// hide other images
$("[image-clicker]").hide();
targetElement.show();
});
};
});
Depending on the surrounding HTML, you may have to modify this. But with JUST the HTML you've shared, you can do this:
$(document).on('click', 'button', function(){
$('img').hide();
$("." + this.id + "_img").show();
//Workaround for '#all'
if( this.id === 'all' ){
$('img').show();
}
});
You could also modify your HTML to take advantage of the data- attribute:
<div id="wrapper">
<div id="buttoncont">
<button data-show="dog, cat" type="button">All</button>
<button data-show="dog" type="button">Dog</button>
<button data-show="cat" type="button">Cat</button>
</div>
<img class="cat_img" src="http://upload.wikimedia.org/wikipedia/commons/2/22/Turkish_Van_Cat.jpg" />
<img class="dog_img" src="http://upload.wikimedia.org/wikipedia/commons/2/26/YellowLabradorLooking_new.jpg" />
</div>
JS (untested, but should work):
$(document).on('click', 'button', function(){
var arrShow = this.getAttribute('data-show').split(/\s*,\s*/);
$('img').hide();
for( var i = arrShow.length; i-- > 0; ){
$('.' + arrShow[i] + '_img').show();
}
});

Categories