jQuery .not() and :not with new classes? [duplicate] - javascript

This question already has answers here:
jQuery click event not working after adding class
(7 answers)
Closed 2 years ago.
I have some card to flip and check if one is equal to another (memory game).
If I flip the card, I don't want that is possible to click and run function if I click on the same card (that is .flipped) or on another that is flipped. But jQuery .not() and :not not working. Maybe I must read another time the DOM after .toggleClass?
$(".card:not('.flipped')").on("click", function () {
$(this).toggleClass("flipped");
if (first) {
firstCard = $(this).attr("game");
first = false;
} else {
secondCard = $(this).attr("game");
first = true;
checkGame();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

The code is binding the events when it is called. So whatver the classes are at that moment in time, is what it finds and binds the event.
So you need to check for the class inside of the method and exit it
$(".card").on("click", function() {
var card = $(this);
if (card.hasClass("flipped")) return;
console.log(this);
card.addClass("flipped");
});
.wrapper {
display: flex;
}
.wrapper > .card {
flex: 1;
text-align: center;
line-height: 50px;
font-size: 30px;
}
.card.flipped {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
<div class="card">5</div>
<div class="card">6</div>
<div class="card">7</div>
<div class="card">8</div>
</div>
Other option is using event delegation where you bind the event to the parent and element and have jQuery do the checking if the class is added yet.
$(".wrapper").on("click", ".card:not('.flipped')", function() {
console.log(this);
var card = $(this);
card.addClass("flipped");
});
.wrapper {
display: flex;
}
.wrapper > .card {
flex: 1;
text-align: center;
line-height: 50px;
font-size: 30px;
}
.card.flipped {
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class="card">1</div>
<div class="card">2</div>
<div class="card">3</div>
<div class="card">4</div>
<div class="card">5</div>
<div class="card">6</div>
<div class="card">7</div>
<div class="card">8</div>
</div>

Apply the click event to all cards.
On click, check if the card is flipped. If it is, do nothing.
Cache your jQuery objects.
$(".card").on("click", function() {
const $card = $(this);
if ($card.hasClass("flipped")) return;
$card.toggleClass("flipped");
// .........
});

you have to check it inside your event 'click'. if the card has class 'flipped' break it
$(".card").on("click", function () {
if($(this).hasClass('.flipped')) return;
$(this).toggleClass("flipped");
if (first) {
firstCard = $(this).attr("game");
first = false;
} else {
secondCard = $(this).attr("game");
first = true;
checkGame();
}
});

Related

Show and hide divs with JS

I have 5 <a>...
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
...and I have 5 <div> and each div have the content of the <a> names. I want show all <a> and show only a <div>, so I want click on a <a> and hide the others divs showing only the selected div.
I'm searching and searching but I can´t find this exactly I found similar things but impossible to integrate to this problem.
This are the divs tags
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
#trips{ display: block };
#eats{ display: none };
#films{ display: none };
#music{ display: none };
#travels{ display: none };
This should do it, though this hides all divs on click, apart from the div matching the a tag. I'd suggest adding something to identify the divs to show/hide. This only requires vanilla js.
const test = document
.querySelectorAll('[id*="show"]')
.forEach(element => element.onclick = () => {
document
.querySelectorAll('div')
.forEach(element => {
element.style.display = 'none';
});
const divIdToShow = element.id.replace('show', '');
const divElementToShow = document.getElementById(divIdToShow);
divElementToShow.style.display = 'block';
});
console.log(test)
#trips {
display: block;
}
#eats {
display: none;
}
#films {
display: none;
}
#music {
display: none;
}
#travels {
display: none;
}
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
This is trivial. You have 2 options: either add "onclick" events on each of your tags and then listen to these events with js. Or, since you added ids to each of your tags, you can now attach click events to them with js.
So, no more words. Here's the simple solution:
document.getElementById("showtrips").onclick = toggleShowForElement(document.getElementById("trips"));
document.getElementById("showeats").onclick = toggleShowForElement(document.getElementById("eats"));
document.getElementById("showfilms").onclick = toggleShowForElement(document.getElementById("films"));
document.getElementById("showmusic").onclick = toggleShowForElement(document.getElementById("music"));
document.getElementById("showtravels").onclick = toggleShowForElement(document.getElementById("travels"));
function toggleShowForElement(element) {
return () => {
if (element.style.display === "none") {
element.style.display = "block";
} else {
element.style.display = "none";
}
}
}
body {
font-family: "Verdana", sans-serif;
}
* {
box-sizing: border-box;
}
a {
cursor: pointer;
color: #008800;
display: inline-block;
padding: 4px;
border-radius: 4px;
}
a:hover {
text-decoration: underline;
}
div {
background-color: #88DD99;
border-radius: 4px;
padding: 5px;
margin-bottom: 5px;
height: 30px;
text-align: center;
}
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
Just added a bit of CSS to make it look a bit prettier. It's not needed here.
If you are using simple javascript you can toggle the div using the following code:
var mydiv = document.getElementById("myDIV");
if (mydiv.style.display === "none") {
mydiv.style.display = "block";
} else {
mydiv.style.display = "none";
}
If you using jQuery you can directly use toggle methods.
$( ".target" ).toggle();
I suggest you group your div and anchor tags using the class name, So it will be easy to maintain.
Your html code will be like:
<a id="showtrips" class="link">TRIPS</a>
<a id="showeats" class="link">EATS</a>
<a id="showfilms" class="link">FILMS</a>
<a id="showmusic" class="link">MUSIC</a>
<a id="showtravels" class="link">TRAVELS</a>
<div id="trips" class="content">Content Trips</div>
<div id="eats" class="content">Content Eats</div>
<div id="films" class="content">Content Films</div>
<div id="music" class="content">Content Music</div>
<div id="travels" class="content">Content Travels</div>
Your JS code will be look like:
document
.querySelectorAll('[class="link"]')
.forEach(element => element.onclick = () => {
document
.querySelectorAll('[class="content"]')
.forEach(element => {
element.style.display = 'none';
});
const mydiv = element.id.substr(4);
document.getElementById(mydiv).style.display = 'block';
});
I just created the working jsfiddle for you link here

Javascript on click event for multiple buttons with same class

I have a few buttons across a site I am building, certain buttons have one class while others have another. What I am trying to do is find the best way to find the clicked button without having an event listener for each individual button. I have come up with the below 2 for loops to find all the buttons with class button-1 and class button-2. Being fairly new to javascript i just don't want to get into bad habits so would appreciate any advice on the best way to achieve this.
<section>
<div class="button--1"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
<section>
<div class="button--2"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
for (var a = 0; a < button1.length; a++) {
button1[a].addEventListener('click',function(){
//do something
});
}
for (var b = 0; b < button2.length; b++) {
button1[b].addEventListener('click',function(){
//do something
});
}
If you plan to have multiple other classes like button--3, …4 … …15,
You must want to target all div elements which class starts (^=) with "button":
(Note that you can do it in the CSS too!)
var allButtons = document.querySelectorAll('div[class^=button]');
console.log("Found", allButtons.length, "div which class starts with “button”.");
for (var i = 0; i < allButtons.length; i++) {
allButtons[i].addEventListener('click', function() {
console.clear();
console.log("You clicked:", this.innerHTML);
});
}
/* Some styling */
section {
margin: 8px 0;
border: 1px solid gray;
}
section div {
border: 1px solid lightgray;
display: inline-block;
margin-left: 8px;
padding: 4px 8px;
width: 30px;
}
section div[class^=button] {
background: lightgray;
cursor: pointer;
}
<span>You can click on the buttons:</span>
<section>
<div class="button--1">s1-1</div>
<div class="button--2">s1-2</div>
<div class="button--3">s1-3</div>
<div class="button--4">s1-4</div>
</section>
<section>
<div class="button--1">s2-1</div>
<div class="button--2">s2-2</div>
<div class="button--3">s2-3</div>
<div class="button--4">s2-4</div>
</section>
<section>
<div class="not-a-button">not1</div>
<div class="not-a-button">not2</div>
<div class="not-a-button">not3</div>
<div class="not-a-button">not4</div>
</section>
Hope it helps.
Try using event delegation
(function() {
document.body.addEventListener("click", clickButtons);
// ^ one handler for all clicks
function clickButtons(evt) {
const from = evt.target;
console.clear();
if (!from.className || !/button--\d/i.test(from.className)) { return; }
// ^check if the element clicked is one of the elements you want to handle
// if it's not one of the 'buttons', do nothing
console.log("you clicked " + from.classList);
}
}())
.button--1:before,
.button--2:before {
content: 'BTTN['attr(class)']';
}
.button--1,
.button--2 {
border: 1px solid #999;
background: #eee;
width: 220px;
padding: 3px;
text-align: center;
}
<section>
<div class="b1 button--1 section1"></div>
<div class="b2 button--1 section1"></div>
<div class="b3 button--2 section1"></div>
</section>
<section>
<div class="b4 button--2 section2"></div>
<div class="b5 button--1 section2"></div>
<div class="b6 button--2 section2"></div>
</section>
You can use multiple selectors in the string of querySelctorAll() by separating them with a ,
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
var allButtons = document.querySelectorAll('.button--1, .button--2');
console.log(button1.length);
console.log(button2.length);
console.log(allButtons.length);
<section>
<div class="button--1"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
<section>
<div class="button--2"></div>
<div class="button--1"></div>
<div class="button--2"></div>
</section>
My suggestion is to use jQuery so that you can do it something like this:
$(document).on('click', '.button--1', function() {
// Do something
});
$(document).on('click', '.button--1', function() {
// Do something
})
But a clean approach for pure Javascript is to create a function that binds a callback for the event.
function bindEvent(callback, eventType, targets) {
targets.forEach(function(target) {
target.addEventListener(eventType, callback);
});
};
var button1 = document.querySelectorAll('.button--1');
var button2 = document.querySelectorAll('.button--2');
bindEvent(function() {
// do something
}, 'click', button1);
bindEvent(function() {
// do something
}, 'click', button2);
The click event is fired when a pointing device button (usually a mouse's primary button) is pressed and released on a single element.
This documentation will help you to understand how it works MDN - Click event

Remove (and Add) Classes to HTML Element with jQuery, While Updating the DOM tree

I'm trying to change a div's attribute class. I have three defined classes and want to cycle through the classes when a user initiates a click event. The first click event works as expected, but the second doesn't show any results.
I've went through a few iterations of trying to get this to work, but have not had any success. I think what's going on is that the DOM tree isn't being updated with the click event, so when the second click event is fired it sees the card-green class, adds the card-yellow class and then exits the branching logic.
$(document).ready(function() {
$('body').on('click', function(event) {
var cardColors = ['card-green', 'card-yellow', 'card-red'];
if ($(event.target.nodeName).attr('class') == 'card-green') {
$(event.target.nodeName).removeClass(event.target.nodeName.className).addClass(cardColors[1]);
} else if ($(this).attr('class') == 'card-yellow') {
$(event.target.nodeName).removeClass(event.target.nodeName.className).addClass(cardColors[2]);
} else {
$(event.target.nodeName).removeClass(event.target.nodeName.className).addClass(cardColors[0]);
}
})
});
Use a switch and toggleClass(). Details are commented in Snippet. No need for an array if you are using a limited number of options. When using $(this) you don't need to keep track of what you clicked (much like event.target except $(this) isn't concerned about events as it is concerned with owner of function.)
SNIPPET
$(document).ready(function() {
$(document).on('click', 'div', function(event) {
/* Determine $(this) class
|| Pass class through the switch
*/
var color = $(this).attr('class');
/* Each part of the switch is a if/else
|| conditional. If the condition isn't
|| met, then it will kick you
|| down to the next conditional and
|| so on, until you reach default or
|| meet a condition in which case the
|| break will kick you out of switch.
|| Each condition has a toggleClass()
|| method to switch colors according
|| to the present class of div
*/
switch (color) {
case 'green':
$(this).toggleClass('green yellow');
break;
case 'yellow':
$(this).toggleClass('yellow red');
break;
case 'red':
$(this).toggleClass('red green');
break;
default:
break;
}
});
});
div {
height: 30px;
width: 50px;
border: 1px solid black;
cursor: pointer;
}
.green {
background: green
}
.red {
background: red;
}
.yellow {
background: yellow
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
<div class='green'></div>
This changes the color in order of the cards array when elements within the document body are clicked:
(Very similar to #gyre's answer, only includes the event.target within the code logic, rather than just the body).
var cards = ['card-green', 'card-yellow', 'card-red'];
$('body').on('click', function() {
var elem = event.target,
curClass = $(elem).attr('class'),
i = cards.indexOf($(elem).attr('class'));
$(elem)
.removeClass(curClass)
.addClass(cards[i = (i + 1) % cards.length]);
});
div {
height: 100px;
width: 100px;
display: inline-block;
}
.card-green {
background-color: green;
}
.card-yellow {
background-color: yellow;
}
.card-red {
background-color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="foo" class="card-green"></div>
<div id="bar" class="card-yellow"></div>
<div id="baz" class="card-red"></div>
Use an additional index variable to keep track of the position in the array:
Demo Snippet:
$(document).ready(function() {
var cardColors = ['card-green', 'card-yellow', 'card-red']
var i = 0
$('body').on('click', function() {
$(this)
.removeClass(cardColors[i])
.addClass(cardColors[i = (i + 1) % cardColors.length])
})
})
body {
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
.card-green { background-color: green; }
.card-yellow { background-color: yellow; }
.card-red { background-color: red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
The same code is workable just remove nodeName from removeClass(event.target.nodeName.className) instead of this use removeClass(event.target.className).
Try this, Its working for me.
$(document).ready(function() {
$('body').on('click', function(event) {
var cardColors = ['card-green', 'card-yellow', 'card-red'];
alert(event.target.className)
if ($(event.target.nodeName).attr('class') == 'card-green') {
$(event.target.nodeName).removeClass(event.target.className).addClass(cardColors[1]);
} else if ($(this).attr('class') == 'card-yellow') {
$(event.target.nodeName).removeClass(event.target.className).addClass(cardColors[2]);
} else {
$(event.target.nodeName).removeClass(event.target.className).addClass(cardColors[0]);
}
})
});

Tie children of one div to children of another div with jquery

I have two parent divs: .inputs and .infoBoxes. Each of them have an equal number of children. When the user clicks into the first .input in .inputs, the first .infoBox in .infoBoxes should slideDown(). Same for second, third, etc. I'd like to do this without re-writing the same code for each pair. So far I have:
var $inputs = $('.inputs').children();
var $infoBoxes = $('.infoBoxes').children();
for(var i = 0; i < $inputs.length; i++ ) {
$($inputs[i]).find('.input').focus(function() {
$($infoBoxes[i]).slideDown();
})
$($inputs[i]).find('.input').blur(function() {
$($infoBoxes[i]).slideUp();
})
}
This isn't working but I have tried replacing i with the indexes of each div.
$($inputs[0]).find('.input').focus(function() {
$($infoBoxes[0]).slideDown();
})
$($inputs[0]).find('.input').blur(function() {
$($infoBoxes[0]).slideUp();
})
repeat...
repeat...
repeat...
This works but isn't very DRY. I'm looking for a better solution that won't have me repeating a bunch of code.
First code will not work, because you using same variable for all internal functions. You should wrap it into function, which will create local variable for index. Try following code:
var $inputs = $('.inputs').children();
var $infoBoxes = $('.infoBoxes').children();
for(var i = 0; i < $inputs.length; i++ ) {
(function(ix) {
$($inputs[ix]).find('.input').focus(function() {
$($infoBoxes[ix]).slideDown();
})
$($inputs[ix]).find('.input').blur(function() {
$($infoBoxes[ix]).slideUp();
})
})(i);
}
slideDown is used for showing elements. I am guessing you want to hide elements, since you are clicking on them and you cant click an hidden element. Use hide or slideUp to hide elements.
$(".input, .infobox").on("click", function() {
var ind = $(this).index();
$(".infobox:eq(" + ind + "), .input:eq(" + ind + ")").hide(500);
});
.input,
.infobox {
widht: 100%;
height: 50px;
text-align: center;
margin: 5px 0;
color: white;
}
.input {
background: red;
}
.infobox {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="inputs">
<div class="input">1</div>
<div class="input">2</div>
<div class="input">3</div>
<div class="input">4</div>
<div class="input">5</div>
</div>
<div class="infoboxes">
<div class="infobox">1</div>
<div class="infobox">2</div>
<div class="infobox">3</div>
<div class="infobox">4</div>
<div class="infobox">5</div>
</div>

Add div below another div

I have a requirement to add 5 divs one by one on each click of a div button. ( the new div should be added below the existing div)
I done the code, but the news ones are getting attached on the top of existing div. please help to correct this.
I have another button which removes the added divs one by one(new ones to be remove first)
here is my code.
<div class="clearFix"></div>
<div id="containershowmore" >
<div id="dragbtnmore" style="cursor: default;">Show more buttons</div>
<div id="dragbtnless" style="cursor: default;">Show Fewer buttons</div>
</div>
<div class="toAdd" style="display:none;" >
<div id="dragdashboardmain" style="cursor: pointer;">dash</div></div>
<div class="toAdd" style="display:none;" >
<div id="dragrcalendar" style="cursor: pointer;">Calendar</div></div>
<div class="toAdd" style="display:none;">
<div id="dragresourcelist" style="cursor: pointer;">Rlist</div></div>
<div class="toAdd" style="display:none;">
<div id="dragdailynotes" style="cursor: pointer;">D Notes</div></div>
<div class="toAdd" style="display:none;">
<div id="dragweeklynotes" style="cursor: pointer;">W Notes</div></div>
script:
$("#dragbtnmore").click(function () {
$('.toAdd').each(function () {
if ($(this).css('display') == 'none') {
$(this).css('display', 'block');
return false;
}
});
var i = 0;
$('.toAdd').each(function () {
if ($(this).css('display') != 'none') {
i++;
}
});
if (i == 5)
$('#dragbtnmore').click(function () { return false; });
});
$("#dragbtnless").click(function () {
$('.toAdd').each(function () {
if ($(this).css('display') == 'block') {
$(this).css('display', 'none');
return false;
}
});
var i = 0;
$('.toAdd').each(function () {
if ($(this).css('display') != 'block') {
i++;
}
});
if (i == 5)
$('#dragbtnless').click(function () { return false; });
$('#dragbtnless').click(function () { return true; });
});
$("#containershowmore").mouseleave(function () {
$(this).hide();
});
function showmore() {
document.getElementById('containershowmore').style.display = "block";
}
style:
#containershowmore
{
margin-top: -75px;position: relative;margin-left: 160px;background-color: #b1dafb;z-index: 1;
width: 125px;
float: right;
padding-left: 5px;
}
.toAdd
{
background-color: blue;
margin-top: -55px;
position: relative;
margin-bottom: 14px;
}
*I referred this Fiddle *
**Solution:
Thankyou Shivam Chopra for helping me . Thanks a TON!! :)
for others, HEre is the solution**
jsfiddle.net/coolshivster/YvE5F/12
Remove margin top from both the div.
#containershowmore
{
position: relative;margin-left: 160px;background-color: #b1dafb;z-index: 1;
width: 125px;
float:right;
padding-left: 5px;
}
#dragbtnmore{
margin-bottom:10px;
border:1px solid black;
}
.toAdd
{
height:20px;
width:70px;
background-color: blue;
position: relative;
margin-bottom: 14px;
}
Then, it will work accordingly.
Here, the code : http://jsfiddle.net/coolshivster/YvE5F/
I have rewritten your code according to your requirement.
Some explanation about the code
I have create a parent div element with id="Add-element" that covers every element which contains class .toAdd .
Then I created data attribute for every div containing class .toAdd .
Now, I display the element one by one. But after first element. Every other element will prepend on the parent div i.e., #Add-element class.
Now, the code which I have rewritten.
jsfiddle link : http://jsfiddle.net/YvE5F/10/

Categories

Resources