with javascript onclick add classname to a div - javascript

I want to achieve with javascript something like when i clink on any of thumbnail (btn-1, btn-2 and btn-3) the specific class should be add to box div dynamically.
my code: JSFiddle
document.getElementById('btn-1').onclick = function() {
document.getElementById('box').className = 'bg-1';
}
#box {
background-color: darkgray;
width: 200px;
height: 200px;
}
.thumbnail {
width: 30px;
height: 30px;
border: 1px solid;
margin: 5px;
position: relative;
float: left;
}
#btn-1 {
background-color: red;
}
#btn-2 {
background-color: green;
}
#btn-3 {
background-color: blue;
}
.bg-1 {
background-color: red;
}
.bg-2 {
background-color: blue;
}
.bg-3 {
background-color: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="box"></div>
<div class="thumbnail" id="btn-1"></div>
<div class="thumbnail" id="btn-2"></div>
<div class="thumbnail" id="btn-3"></div>

You javascript is working, but your CSS isn't.
You need to add !important as follows to .bg-1, .bg-2 and .bg-3
.bg-1 {
background-color: red !important;
}
Otherwise the id styling is taking preference over the class styling
You can see the classname is being added if you right click on the grey div and choose inspect element in Chrome.

Instead of bothering with classes, use simply a data- attribute like: data-bg="#f00"
$('[data-bg]').css('background', function () {
$(this).on('click', () => $('#box').css('background', this.dataset.bg));
return this.dataset.bg;
});
#box {
background: darkgray;
width: 120px; height: 120px;
}
[data-bg] {
width: 30px; height: 30px;
margin: 5px;
float: left;
}
<div id="box"></div>
<div data-bg="red"></div>
<div data-bg="#00f"></div>
<div data-bg="rgb(255,0,180)"></div>
<div data-bg="linear-gradient(to right, #E100FF, #7F00FF)"></div>
<script src="//code.jquery.com/jquery-3.1.0.js"></script>

You want to use jquery .addClass() function:
$('.myButton').addClass('myNewClass');
The function would probably look something like this:
$(function () {
$('.thumbnail').click(function() {
$('#box').addClass($(this).attr('id'));
});
})

You can get all the thumbnails as an array, and then iterate through the array and dynamically add an event listener to each, which will add the desired className to box when clicked:
var thumbnails = document.getElementsByClassName('thumbnail');
Array.from(thumbnails).forEach(function(thumbnail) {
var id = thumbnail.id;
thumbnail.addEventListener('click', function() {
document.getElementById('box').className = id.replace('btn', 'bg')
});
});

Related

jQuery/JavaScript if statement for two toggles

I have two toggles (toggle-1 and toggle-2) with different contents in a header. I would like to prevent the user to have both toggles active simultaneously (otherwise they overlap).
In the code below I tried to use if statements to hide one of the toggles if the other is already opened but it does not work.
Ideally, what I would like to happen is that if toggle-1 is active and the user clicks on toggle-2, then toggle-1 would come back to its original state and toggle-2 would be now active. The same the other way around.
I am not familiar with JavaScript yet and I'd really appreciate if you could tell me what I have done wrong and how it should be done to have my ideal result
Here's the link to my CodePen if you find it easier:
https://codepen.io/fergos2/pen/NWWxgEp
var myToggle
var oneToggle = $(document).ready(function() {
$('.toggle-1').click(function() {
$('.toggle-1').toggleClass('active')
$('.toggle-1-content').toggleClass('active')
})
})
var twoToggle = $(document).ready(function() {
$('.toggle-2').click(function() {
$('.toggle-2').toggleClass('active')
$('.toggle-2-content').toggleClass('active')
})
})
if (myToggle == oneToggle) {
$(document).ready(function() {
$('toggle-2-content').hide();
})
} else if (myToggle == twoToggle) {
$('toggle-1-content').hide();
}
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle-1,
.toggle-2 {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.toggle-1.active,
.toggle-2.active {
background-color: red;
}
.toggle-1-content,
.toggle-2-content {
display: none;
}
.toggle-1-content.active,
.toggle-2-content.active {
display: block;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
.toggle-1-content.active {
left: 0;
}
.toggle-2-content.active {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle-1">1</div>
<div class="toggle-1-content">
<p>Some content 1</p>
</div>
<div class="toggle-2">2</div>
<div class="toggle-2-content">
<p>Some content 2</p>
</div>
</div>
</div>
Several issues.
Please study the code below
too many $(document.ready... and no need to store the result of such a statement
Using a data-attribute and a common class, shortens the code a lot. DRY Don't repeat yourself
I simplified the content containers CSS too
$(function() { // on page load
$('.toggle').on("click", function() { // any of the toggles
const $wrapper = $(this).closest(".wrapper");
const id = $(this).data("id");
$(this).toggleClass('active'); // toggle clicked div
const show = $(this).is(".active"); // is it active after we toggled?
$wrapper
.find(".toggle") // find all toggles
.not(this) // exclude the one we clicked
.removeClass("active"); // remove class
$wrapper.find(".content").hide(); // hide any content divs
$("#" + id).toggle(show); // show the one belonging to the clicked toggle
})
})
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.active {
background-color: red;
}
.content {
display: none;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
#div1 {
left: 0;
}
#div2 {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle" data-id="div1">1</div>
<div id="div1" class="content">
<p>Some content 1</p>
</div>
<div class="toggle" data-id="div2">2</div>
<div id="div2" class="content">
<p>Some content 2</p>
</div>
</div>
</div>
Working code:
$(document).ready(function() {
$('.toggle-1').click(function() {
if ($('.toggle-2').hasClass('active')) {
// remove toggle-2 active classes
$('.toggle-2').removeClass('active');
$('.toggle-2-content').removeClass('active');
}
$('.toggle-1').toggleClass('active');
$('.toggle-1-content').toggleClass('active');
});
$('.toggle-2').click(function() {
if ($('.toggle-1').hasClass('active')) {
// remove toggle-1 active classes
$('.toggle-1').removeClass('active');
$('.toggle-1-content').removeClass('active');
}
$('.toggle-2').toggleClass('active');
$('.toggle-2-content').toggleClass('active');
});
});
Here is the link to my working version.
A few things to keep in mind:
You don't need to call $(document).ready() multiple times. There's just no reason to call it multiple times on a single page as the event is only fired once.
You need to keep track of state somehow; hence the if ($('el').hasClass('classname')) syntax. Once you handle that properly, it's easy to ensure that each element is 'reset' to its original state when the other is clicked.
Hope that helps!
toggleClass accepts a second boolean parameter that forces the type of toggle, on or off. More than that you can also target multiple elements with a single jQuery call, so use that to your advantage since the classes applied have the same name.
So you could simplify your code to
$(document).ready(function() {
$('.toggle-1').click(function() {
$('.toggle-1, .toggle-1-content').toggleClass('active');
$('.toggle-2, .toggle-2-content').toggleClass('active', false)
})
$('.toggle-2').click(function() {
$('.toggle-2, .toggle-2-content').toggleClass('active');
$('.toggle-1, .toggle-1-content').toggleClass('active', false)
})
})
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle-1,
.toggle-2 {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.toggle-1.active,
.toggle-2.active {
background-color: red;
}
.toggle-1-content,
.toggle-2-content {
display: none;
}
.toggle-1-content.active,
.toggle-2-content.active {
display: block;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
.toggle-1-content.active {
left: 0;
}
.toggle-2-content.active {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle-1">1</div>
<div class="toggle-1-content">
<p>Some content 1</p>
</div>
<div class="toggle-2">2</div>
<div class="toggle-2-content">
<p>Some content 2</p>
</div>
</div>
</div>
You can use the method "removeClass" to remove the active class from the other toggle
var oneToggle = $(document).ready(function() {
$(".toggle-1").click(function() {
$(".toggle-1").toggleClass("active")
$(".toggle-1-content").toggleClass("active")
$(".toggle-2").removeClass("active")
$(".toggle-2-content").removeClass("active")
})
})
var twoToggle = $(document).ready(function() {
$(".toggle-2").click(function() {
$(".toggle-1").removeClass("active")
$(".toggle-1-content").removeClass("active")
$(".toggle-2").toggleClass("active")
$(".toggle-2-content").toggleClass("active")
})
})

Toggle background color of same div

I am trying to toggle the background color of same div.
It does changes once (from blue to red) as expected.
But it is not able to toggle back to red and continue toggling between the 2 colors. I know I should use "==" in the first if-statement but when using "==" not even the first toggle works.
Any suggestions how to get the toggle to work repetitive?
function toggleFunction() {
var x = document.getElementById("box");
if (x.style.background == "blue") {
x.style.background = "red";
} else {
x.style.background = "blue";
}
}
.box {
background-color: blue;
width: 100px;
height: 100px;
margin: 30px 0px 0px 30px;
}
<div id="box" class="box" onclick="toggleFunction()"></div>
The simplest solution would to create a new class called red and toggle that using classList.toggle. The main advantage of this approach would be that you can toggle more CSS properties, if you use a class for toggling and this will also deduct the if-else comparison for you.
function toggleFunction() {
var x = document.getElementById("box");
x.classList.toggle("red");
}
.box {
background-color: blue;
width: 100px;
height: 100px;
margin: 30px 0px 0px 30px;
}
.red{
background-color: red;
}
<div id="box" class="box" onclick="toggleFunction()"></div>
$( ".box" ).each(function() {
$( this).click(function() {
$( this ).toggleClass( "red" );
});
});
.box {
background: blue;
width: 100px;
height: 100px;
margin: 30px 0px 0px 30px;
}
.box.red {
background:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="box" class="box"></div>
<div id="box" class="box"></div>

How to specify different duration for slideUp and slideDown in jquery slideToggle?

I want a full width panel to slide down from the top of the browser, that will display my contact details, along with social links etc:
$(document).ready(function() {
$("#flip").click(function() {
$("#panel").slideToggle();
});
});
#flip {
padding: 5px;
width: 300px;
text-align: center;
background-color: #e5eecc;
border: solid 1px #c3c3c3;
}
#panel {
padding: 50px;
display: none;
width: 100%;
height: 200px;
z-index: 5000;
background-color: black;
}
.f {
position: static;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="panel">Hello world!</div>
<div id="flip">
<span class="f">MENU</span>
</div>
This works a treat, but how can I specify different times for slide up and slidedown?
You can store duration of animation in variable and use it. In function of callback of slideToggle() change duration.
var duration = 500;
$("#flip").click(function() {
$("#panel").slideToggle(duration, function(){
duration = $(this).is(":visible") ? 2000 : 500;
});
});
var duration = 500;
$("#flip").click(function() {
$("#panel").slideToggle(duration, function(){
duration = $(this).is(":visible") ? 2000 : 500;
});
});
#flip {
width: 300px;
text-align: center;
background-color: #e5eecc;
border: solid 1px #c3c3c3;
}
#panel {
display: none;
width: 100%;
height: 150px;
background-color: black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="panel">Hello world!</div>
<div id="flip">
<span class="f">MENU</span>
</div>
EDIT
I have edit my answer to reflect the change in the question.
Use slideDown() and slideUp() instead.
CODEPEN
http://codepen.io/alexincarnati/pen/PWOPjY
In case you want to add different durations to sliding up and down in jQuery then you can simply add a flag and check if the menu is opened or not and then use slideDown() and slideUp() as methods.
That way you could add different durations to the slide.
$(document).ready(function() {
var menuOpened = false;
$("#flip").click(function() {
if (menuOpened === false) {
$("#panel").slideDown(1000, function() {
menuOpened = true;
});
} else {
$("#panel").slideUp(700, function() {
menuOpened = false;
});
}
});
});
Simply just add the time as a parameter to the slideToggle method.
You can see in the docs this:
slideToggle( [duration ] [, complete ] )
duration (default: 400)
Type: Number or String
A string or number determining how long the animation will run.
complete
Type: Function()
A function to call once the animation is complete, called once per matched element.
$(document).ready(function() {
$("#flip").click(function() {
$("#panel").slideToggle(3000);
});
});
#flip {
padding: 5px;
width: 300px;
text-align: center;
background-color: #e5eecc;
border: solid 1px #c3c3c3;
}
#panel {
padding: 50px;
display: none;
width: 100%;
height: 200px;
z-index: 5000;
background-color: black;
}
.f {
position: static;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="panel">Hello world!</div>
<div id="flip">
<span class="f">MENU</span>
</div>
You can read more in the official documentation here.
UPDATE:
If you want to have different durations for slideUp() and slideDown() methods you can do something like this:
$(document).ready(function() {
var check_state = false;
$("#flip").click(function() {
if (check_state === false) {
$("#panel").stop().slideDown(3000);
check_state = true;
} else {
$("#panel").stop().slideUp(1500);
check_state = false;
}
});
});
#flip {
padding: 5px;
width: 300px;
text-align: center;
background-color: #e5eecc;
border: solid 1px #c3c3c3;
}
#panel {
padding: 50px;
display: none;
width: 100%;
height: 200px;
z-index: 5000;
background-color: black;
}
.f {
position: static;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="panel">Hello world!</div>
<div id="flip">
<span class="f">MENU</span>
</div>
You can specify the time in the first parameter of the function slideToggle:
$("#panel").slideToggle(4000);
You can read about .slideToggle() on jquery documentation
As it say the slideToggle() function accept two parameters:
.slideToggle( [duration ] [, complete ] )
The first is for the duration (in millisecond) and the second is the callback after the animation is complete
$(document).ready(function() {
$("#flip").click(function() {
$("#panel").slideToggle(5000 /* Here is the duration of the animation in MS */, function() {
//Do what you want here
});
});
});
#flip {
padding: 5px;
width: 300px;
text-align: center;
background-color: #e5eecc;
border: solid 1px #c3c3c3;
}
#panel {
padding: 50px;
display: none;
width: 100%;
height: 200px;
z-index: 5000;
background-color: black;
}
.f {
position: static;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="panel">Hello world!</div>
<div id="flip">
<span class="f">MENU</span>
</div>

Continue propagation

I have some nested elements on my page with a same handler on them which should be called only for an event target without affecting elements higher in DOM tree. To achieve this behavior I used stopPropagation method and it was ok. Then I had to add some handlers for body and other elements outside the nested divs which should be called in any case. Of course stopPropagation isn't an option now but how can I make it work?
Here is a sample:
html:
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="no-handler"></div>
</div>
</div>
</div>
</div>
css:
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#no-handler {
width: 150px;
height: 150px;
padding: 30px;
border: solid 1px #888;
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
js:
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function nestedHandler(e) {
e.stopPropagation();
this.classList.add("nested-clicked");
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
jsfiddle link:
http://jsfiddle.net/6kgnu7fr/
clicking on .nested should add red background color to clicked element and add green color to outer body and #container
UPD:
http://jsfiddle.net/6kgnu7fr/2/
clicking on #no-event or any other element inside .nested should also call nestedHandler for this .nested element.
You can check for the event's target in your nestedHandler instead of stopping the propagation. Change the class only if the target is this so that the effet will only be applied for the div on which the event occurred:
function nestedHandler(e) {
if (e.target === this) {
this.classList.add("nested-clicked");
}
}
Edit
Following your edit, this is harder. Way to do it is to find e.target's first ancestor with the "nested" class, then doing the comparison with it instead of target:
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
This is naïve shot. You may want to look for a way to make it more efficient, e.g. by avoiding to look for the first ancestor on each .nested div.
See the working snipped below.
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
function nestedHandler(e) {
var nestedParent = findAncestorWithClass(e.target, "nested");
if (this === nestedParent) {
nestedParent.classList.add("nested-clicked");
}
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#sub-nested {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="sub-nested"></div>
</div>
</div>
</div>
</div>

Add class to matching element without other specific class

Whenever a user clicks on the body I would like to add a class to a certain element, as long as that element doesn't have a specific class. The issue is, I re-use this element and some of these elements will have that specific class I mentioned and other will not. If one element has this class, with my current code, no element will have new class added.
Here is a fiddle showing the behaviour.
Example:
$('body').on('click', function(){
if ($('.box').hasClass('red') && !$('.box').hasClass('stay-red')) {
$('.box').addClass('blue');
}
});
html, body {
background: lightblue;
height: 100%;
}
.box {
height: 20px;
width: 20px;
margin-bottom: 10px;
}
.red {
background: red;
}
.blue {
background: blue;
}
<div class="box red stay-red"></div>
<div class="box red"></div>
It will be a lot easier with filter, and will avoid your problem:
$('body').on('click', function(){
$('.box').filter('.red:not(.stay-red)').addClass('blue');
});
html, body {
background: lightblue;
height: 100%;
}
.box {
height: 20px;
width: 20px;
margin-bottom: 10px;
}
.red {
background: red;
}
.blue {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div class="box red stay-red"></div>
<div class="box red"></div>
</body>
</html>
$('.box.red:not(.stay-red)').addClass('blue');
Is that what you want?
:not() Selector https://api.jquery.com/not-selector/
https://jsfiddle.net/7L3ub1sp/
$(function(){
$('body').on('click', function(){
$('.box').each(function(){
if ($(this).hasClass('red') && !$(this).hasClass('stay-red')) {
$(this).addClass('blue');
}
})
});
})
html, body {
background: lightblue;
height: 100%;
}
.box {
height: 20px;
width: 20px;
margin-bottom: 10px;
}
.red {
background: red;
}
.blue {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<div class="box red stay-red"></div>
<div class="box red"></div>
</body>
</html>
You can use:
$('body').on('click', function(){
$('.box.red:not(.stay-red)').addClass('blue');
});
Or loop through all of them with each:
$('body').on('click', function(){
$('.box').each(function() {
if ($(this).hasClass('red') && !$(this).hasClass('stay-red')) {
$(this).addClass('blue');
}
});
});
If you want to avoid adding the new class to all elements if one of them has the other class, you can use some (or every):
var els = document.getElementsByClassName('box');
if(![].some.call(els, function(el) {
return el.classList.contains('stay-red');
}) [].forEach.call(els, function(el) {
return el.classList.add('blue');
});
Here is the body onclick you need:
$('body').on('click', function(){
$('.box.red:not(.stay-red)').removeClass('red').addClass('blue');
});
You also need to remove the class .red, even if in this precise case it works because the class .blue is defined after the class .red in the CSS

Categories

Resources