Check data attribute values in multiple divs with click function - javascript

I'm trying to get a selected group of divs to change the background color based off of data set in the buttons data attribute.
I've done some digging, but I'm getting confused on how to pass the conditional against the title in the div and the data in the data attribute from the button.
I know I have to possibly .split() the data out of the button since there is more than one data attribute for each button. But then getting that info and getting to check again the set of divs is where I think I'm getting hung up on.
Here's what I have so far:
Codepen Link: https://codepen.io/ultraloveninja/pen/bmvOYB
HTML:
<section class="state-group">
<div class="state" title="Illinois">
<h2>Illinois</h2>
</div>
<div class="state" title="New Hampshire">
<h2>New Hampshire</h2>
</div>
<div class="state" title="Washington">
<h2>Washington</h2>
</div>
<div class="state" title="North Dakota">
<h2>North Dakota</h2>
</div>
<div class="state" title="South Dakota">
<h2>South Dakota</h2>
</div>
<div class="state" title="Wisconsin">
<h2>Wisconsin</h2>
</div>
</section>
<section class="btn-group">
<a data-state='New Hampshire,Illinois,Wisconsin' class="region region-one" href="">Region 1</a>
<a data-state='Illinois,Washington,North Dakota' class="region region-two" href="">Region 2</a>
<a data-state='Washington,North Dakota,South Dakota' class="region region-three" href="">Region 3</a>
</section>
JS:
var $region = $('.region').data("state");
var $single = $region.split(',');
$(".region").on("click", function(e) {
$(".state-group div").each(function() {
var $state = $(this).attr("title");
if ($state == $single ) {
$(this).css('background-color','blue')
}
});
e.preventDefault();
});
Basically, once you click the button it will check the data from the button you clicked on, find the title of the div (in this case the state) and if it matches, make the background of those specific divs blue.
Again, not sure if I'm going about this the correct way, or if I need to get the data from the divs and store that in a variable as well. Hope that make sense.

You should get state from the clicked button, not when js load. So that you will have states based on the clicked button.
$(".region").on("click", function(e) {
//Below line is important; Otherwise it won't work for other buttons.
var $single = $(this).data("state").split(",");
$(".state-group div").each(function() {
var $state = $(this).attr("title");
if ($single.indexOf($state) > -1) {
$(this).css('background-color','blue');
}else{
$(this).css('background-color','#ccc');
}
});
e.preventDefault();
});
https://codepen.io/anon/pen/PyRgEZ

Here's what you need to do:
var $region = $('.region').data("state");
var $single = $region.split(',');
$(".region").on("click", function(e) {
$(".state-group div").each(function() {
var $state = $(this).attr("title");
// HERE
if ($single.includes($state)) {
$(this).css('background-color','blue')
}
});
e.preventDefault();
});

You may not need to keep reference of each state in the data, rather than use data-state & use the same value as class of the relevant element.So on click get the data-state & from the dom get the element which have same class and highlight them.
The benefit of this length of the value of data-state will not increase with increase of new element
$(".region").on("click", function(e) {
e.preventDefault();
let getDataState = $(this).data('state')
$('.' + getDataState).css('background-color', 'blue')
});
body {
padding: 20px;
}
.state-group {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.state {
flex: 0 1 13%;
display: flex;
justify-content: center;
align-items: center;
min-height: 200px;
background: #ccc;
font-size: 16px;
text-align: center;
padding: 10px;
}
.btn-group {
display: flex;
flex-direction: column;
a {
margin: 10px 0;
text-decoration: none;
display: block;
padding: 15px 5px;
background: lighten(blue, 20%);
color: white;
text-align: center;
width: 100%;
max-width: 150px;
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="state-group">
<div class="state btn-1 btn-2" title="Illinois">
<h2>Illinois</h2>
</div>
<div class="state btn-1" title="New Hampshire">
<h2>New Hampshire</h2>
</div>
<div class="state btn-2 btn-3" title="Washington">
<h2>Washington</h2>
</div>
<div class="state btn-2 btn-3" title="North Dakota">
<h2>North Dakota</h2>
</div>
<div class="state btn-3" title="South Dakota">
<h2>South Dakota</h2>
</div>
<div class="state btn-1" title="Wisconsin">
<h2>Wisconsin</h2>
</div>
</section>
<section class="btn-group">
<a data-state='btn-1' class="region region-one" href="">Region 1</a>
<a data-state='btn-2' class="region region-two" href="">Region 2</a>
<a data-state='btn-3' class="region region-three" href="">Region 3</a>
</section>

Related

Two Column Accordion with Separate Full Width Divs

The intension is to have a two column accordion, without limiting the "expand" field to the left or right column. The catch is that there will be multiple on one page. This is already created, but only button 1 is working. With the way my JS is going, it will get very very repetitive - I am looking for assistance with re-writing the JS to be multiple click friendly. Fiddle: https://codepen.io/ttattini/pen/abLzaaY
EDIT: It would also be perfect if one dropdown would close as the next is opened
HTML
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="row">
<div id="column">
<button id="button">I am Button #1</button>
<button id="button">I am Button #3</button>
</div>
<div id="column">
<button id="button">I am Button #2</button>
<button id="button">I am Button #4</button>
</div>
</div>
<div id="hidden">
<p id="content"> So here I am #1</p>
</div>
<div id="hidden">
<p id="content"> So here I am #2</p>
</div>
<div id="hidden">
<p id="content"> So here I am #3</p>
</div>
<div id="hidden">
<p id="content"> So here I am #4</p>
</div>
CSS
#hidden {
background: #ccc;
margin-top: 2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
#button {
padding: 10px;
margin-top: 5px;
width:50%;
margin-left: 10%;
cursor: pointer;
}
#row {
display: flex;
}
#column {
flex: 50%;
}
JS
$(function() {
var b = $("#button");
var w = $("#hidden");
var l = $("#content");
b.click(function() {
if (w.hasClass('open')) {
w.removeClass('open');
w.height(0);
} else {
w.addClass('open');
w.height(l.outerHeight(true));
}
});
});
The biggest issue is that you're using IDs when you should be using classes. IDs must be unique to each element in a page. When you repeat an ID, JS will only target the first element using that ID. That's why only the first one is working.
The second issue is that, because of the way the script is written, it will only target a single element. What you need to do is get all the elements you want to target by something like their class name and then loop through them, applying the event listener to each one and its appropriate children.
EDIT: Here is an example from some code I wrote for a page with multiple accordions a few weeks ago in vanilla JS
//Below I establish a counting variable and find all the accordions on the page
const acc = document.getElementsByClassName( 'accordion' );
let i;
//Looping through each accordion
for ( i = 1; i <= acc.length; i++ ) {
//Identify target for the event listener. In this case, a heading for each accordion, which I've numbered e.g. "title-1"
const title = 'title-' + i;
const label = document.getElementById( title );
//Identify target content, in this case a list that has a unique ID e.g. "list-1"
const listNum = 'list-' + i;
const list = document.getElementById( listNum );
//Add event listener to heading that toggles the active classes
label.addEventListener( 'click', function() {
label.classList.toggle( 'accordion--active' );
});
}
Of course, there's more than one way to skin a cat, but this is a working example.
I have tracked the clicked event of each button and showed the corresponding hidden content with the use of data- attribute.
I have used vanilla JavaScipt instead of jQuery.
const buttons = document.querySelectorAll('.button');
const hiddens = document.querySelectorAll('.hidden');
buttons.forEach((btn) => {
btn.addEventListener('click', btnClicked)
function btnClicked(e) {
hiddens.forEach((hidden) => {
if(e.target.dataset.btn == hidden.dataset.content) {
hidden.classList.toggle('height')
} else {
hidden.classList.remove('height')
}
})
}
})
.hidden {
background: #ccc;
margin-top: 2%;
padding-left:2%;
overflow: hidden;
transition: height 200ms;
height: 0; /* <-- set this */
}
.hidden.height {
height: 50px;
}
.button {
padding: 10px;
color: white;
background-color: #2da6b5;
border: none;
margin-top: 5px;
width:90%;
margin-left: 5%;
cursor: pointer;
}
.button:hover {
filter: brightness(.9);
}
#row {
display: flex;
}
.column {
flex: 50%;
}
<div id="row">
<div class="column">
<button class="button" data-btn="one">I am Button #1</button>
<button class="button" data-btn="three">I am Button #3</button>
</div>
<div class="column">
<button class="button" data-btn="two">I am Button #2</button>
<button class="button" data-btn="four">I am Button #4</button>
</div>
</div>
<div class="hidden" data-content="one">
<p class="content"> So here I am #1</p>
</div>
<div class="hidden" data-content="two">
<p class="content"> So here I am #2</p>
</div>
<div class="hidden" data-content="three">
<p class="content"> So here I am #3</p>
</div>
<div class="hidden" data-content="four">
<p class="content"> So here I am #4</p>
</div>
Also, please do not use the same ID at multiple elements.

JavaScript Show invisible divs on click

I ran into a problem that when I click on the button, it just flips the icon but only makes the invisible fields visible on the second click. Are there any idea how to do it?
(Heres a gif to show my problem: https://ibb.co/cvz7pWC )
Also heres my code :
function moreSoc() {
var moresoc = document.getElementById("moresoc");
var btnText = document.getElementById("mbtn");
if (moresoc.style.display === "none" ) {
moresoc.style.display = "block";
mbtn.innerHTML = "More ▲";
} else {
moresoc.style.display = "none";
mbtn.innerHTML = "More ▼"
}
}
.morebutton {
border: none;
background: #fff;
color: #111;
font-size: 32px;
}
#moresoc {
display: none;
}
<div class="wrapper more">
<button class="morebutton" id="mbtn" onclick="moreSoc()">More ▲</button>
</div>
<section class="social-links" id="moresoc">
<div class="wrapper">
<h2>Others</h2>
<div class="social-link facebook">
<p>Facebook</p>
</div>
<div class="social-link instagram">
<p>Instagram</p>
</div>
<div class="social-link twitter">
<p>Twitter</p>
</div>
<div class="social-link youtube">
<p>Youtube</p>
</div>
</div>
</section>
This could be to do with you not being to read element.style.display as none the first time round. This is because it has not yet been set by JavaScript, but just by css. I suggest changing your if statement to check for not "block".
function moreSoc() {
var moresoc = document.getElementById("moresoc");
var btnText = document.getElementById("mbtn");
if (moresoc.style.display != "block" ) {
moresoc.style.display = "block";
mbtn.innerHTML = "More ▲";
} else {
moresoc.style.display = "none";
mbtn.innerHTML = "More ▼"
}
}
.morebutton {
border: none;
background: #fff;
color: #111;
font-size: 32px;
}
#moresoc {
display: none;
}
<div class="wrapper more">
<button class="morebutton" id="mbtn" onclick="moreSoc()">More ▼</button>
</div>
<section class="social-links" id="moresoc">
<div class="wrapper">
<h2>Others</h2>
<div class="social-link facebook">
<p>Facebook</p>
</div>
<div class="social-link instagram">
<p>Instagram</p>
</div>
<div class="social-link twitter">
<p>Twitter</p>
</div>
<div class="social-link youtube">
<p>Youtube</p>
</div>
</div>
</section>
ElementCSSInlineStyle.style only returns (or sets) inline styles on an element. On your first click there is no inline display property to read so your condition sets it to none. On the second click your condition finds none and sets it to block.
The answer to look for !block solves this immediate problem but it stills ties your styling to your js rather than keeping it in your CSS. This means that if the default display property of your div needs to change in your layout (inline-block, flex, etc) you would need to change it in your js as well as your CSS.
For this reason I would recommend not using inline styles at all but rather rather use Element.classList to manage applied styles from your CSS – in this case just the adding/removing of a .hidden class that sets display to none without having to know what the appropriate visible display default is.
Also, since you are querying the button element in your code anyway, it would be better to apply the click listener from your js as well rather than inline.
function moreSoc() {
const moresoc = document.getElementById("moresoc");
if (moresoc.classList.contains('hidden')) {
moresoc.classList.remove('hidden');
mbtn.innerHTML = "More ▲";
} else {
moresoc.classList.add('hidden');
mbtn.innerHTML = "More ▼"
}
}
const mbtn = document.getElementById("mbtn");
mbtn.addEventListener('click', moreSoc);
.morebutton {
border: none;
background: #fff;
color: #111;
font-size: 32px;
}
#moresoc {
}
.hidden {
display: none;
}
<div class="wrapper more">
<button class="morebutton" id="mbtn">More ▲</button>
</div>
<section class="social-links hidden" id="moresoc">
<div class="wrapper">
<h2>Others</h2>
<div class="social-link facebook">
<p>Facebook</p>
</div>
<div class="social-link instagram">
<p>Instagram</p>
</div>
<div class="social-link twitter">
<p>Twitter</p>
</div>
<div class="social-link youtube">
<p>Youtube</p>
</div>
</div>
</section>

Change styles to toggled divs

Im using filtered divs and I want to change the flex-direction of each section only when that specific section is toggled, then go back to original styles when going back to "Show all"
Here is the link for the filtered divs
https://www.w3schools.com/howto/tryit.asp?filename=tryhow_js_filter_elements
The W3 Schools code is not very good. It can be improved greatly but the use of data attributes, proper event listeners and event bubbling
All we need to to to get your "flex switch" happening is add/remove a class to the ".container" to indicate if filtered or not
var container = document.querySelector(".container");
//Add an event listener to the div containing the buttons
document.getElementById("myBtnContainer").addEventListener("click", function(event) {
//remove active class from previous active button
this.querySelector(".active").classList.remove("active");
//add active class to clicked item
event.target.classList.add("active");
//Add filitered to ".container" if "All" clicked, remove otherwise
container.classList.toggle("filtered", event.target.dataset.target !== "all")
//Display chosen elements
var elements = container.querySelectorAll(".filterDiv");
for (var i = 0; i < elements.length; i++) {
//Long version of below
//var categoryArray = elements[i].dataset.category.split(",");
//var hasTargetCategory = categoryArray.includes(event.target.dataset.target);
//elements[i].classList.toggle("show",hasTargetCategory);
elements[i].classList.toggle("show", elements[i].dataset.category.split(",").includes(event.target.dataset.target));
}
})
.filterDiv {
background-color: #2196F3;
color: #ffffff;
width: 100px;
line-height: 100px;
text-align: center;
margin: 2px;
flex: none;
}
.container {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
}
/* Style the buttons */
.btn {
border: none;
outline: none;
padding: 12px 16px;
background-color: #f1f1f1;
cursor: pointer;
}
.btn:hover {
background-color: #ddd;
}
.btn.active {
background-color: #666;
color: white;
}
/*Class to change flex direction*/
.filtered {
flex-direction: column;
}
/*Hide elements without the show class*/
.filtered>.filterDiv:not(.show) {
display: none;
}
<div id="myBtnContainer">
<button class="btn active" data-target="all"> Show all</button>
<button class="btn" data-target="cars"> Cars</button>
<button class="btn" data-target="animals"> Animals</button>
<button class="btn" data-target="fruits"> Fruits</button>
<button class="btn" data-target="colors"> Colors</button>
</div>
<div class="container">
<div class="filterDiv" data-category="cars">BMW</div>
<div class="filterDiv" data-category="colors,fruits">Orange</div>
<div class="filterDiv" data-category="cars">Volvo</div>
<div class="filterDiv" data-category="colors">Red</div>
<div class="filterDiv" data-category="cars,animals">Mustang</div>
<div class="filterDiv" data-category="colors">Blue</div>
<div class="filterDiv" data-category="animals">Cat</div>
<div class="filterDiv" data-category="animals">Dog</div>
<div class="filterDiv" data-category="fruits">Melon</div>
<div class="filterDiv" data-category="fruits,animals">Kiwi</div>
<div class="filterDiv" data-category="fruits">Banana</div>
<div class="filterDiv" data-category="fruits">Lemon</div>
<div class="filterDiv" data-category="animals">Cow</div>
</div>
I didn't understand what you meant by "flexible steering", would it be to replace the phrase "Show all" with the active section? This can be done with jquery, but you would have to replace the element that houses the text.
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button" id="all">Show all</button>
<button type="button" id="cars">cars</button>
<script>
$("#cars").on("click", function(){
$("#all").text('Cars');
});
$("#all").on("click", function(){
$("#all").text('Show All');
});
</script>
Alternative without jquery
<button id="all">Show All</button>
<button id="cars" onclick="cars()">Cars</button>
<script>
function cars() {
document.getElementById("all").innerHTML = "Cars";
}
</script>
Alternative to build the tab without so much javascript and css
Bootstrap Collapse: You can use Bootstrap Colapse which already has a structure ready and you would only have to organize the HTML with the divs as per the documentation: https://getbootstrap.com/docs/4.0/components/collapse/
Javascript behavior: Like the previous one, this is a tabbed browsing API, which is identical to the code you are using, with the difference that here you would be using the Bootstrap structure and will not require many modifications, you should note the similarity between this and the previous one, in fact what changes is only the usability, the situations in which you will use one or the other, but the purpose is the same, both seek to hide elements.
https://getbootstrap.com/docs/4.0/components/navs/#javascript-behavior

Unable to make button show or hide depending on whether another element has content

So this is probably a simple question. But I have to ask because it's not doing what I want.
Here is the button JavaScript:
$(document).ready(function(){
if ($(".saved-items").html().length > 0) {
$('.btn-01').show();
}
});
The button is shown if there is content in the div. But I would like it to hide again if the div has no content.
I tried:
$(document).ready(function(){
if ($(".saved-items").html().length > 0) {
$('.btn-01').show();
}
if ($(".saved-items").html().length < 0) {
$('.btn-01').hide();
}
});
Here is the HTML when an item is added:
<div class="col-sm-2">
<div class="saved-items"><h4>Items:</h4>
<ul>
<li><i class="fa fa-anchor" aria-hidden="true" style="color:#f60;margin-right:10px;"></i>RAW</li>
</ul>
<script>
$(document).ready(function(){
$('.btn-01').toggle($(".saved-items").html().trim().length > 0);
});
</script>
</div>
</div>
<div class="col-sm-2">
<a class="fancybox my-subject" href="#contact-formulier" value="Item X"><div style="display: block;" class="btn-01">Check Out</div></a>
</div>
</div>
And this is the HTML without any items saved:
<div class="col-sm-2">
<div class="saved-items">
<h4>Items:</h4>
</div>
</div>
<div class="col-sm-2">
<a class="fancybox my-subject" href="#contact-formulier" value="Item X"><div class="btn-01">Check Out</div></a>
</div>
But no go. Here is the CSS of btn-01:
.btn-01 {
background: #f60;
color: #fff;
border-radius: 2px !important;
padding: 5px;
text-align: center;
margin: 40px auto 0px auto;
width: 90%;
display: none;
border:none;
}
You can use toggle() to achieve this:
$('.btn-01').toggle($(".saved-items").html().trim().length > 0);
Working example
length of string is zero or greater than zero..can't be less than zero.
$(document).ready(function(){
if ($(".saved-items").html().length > 0) {
$('.btn-01').show();
}else{
$('.btn-01').hide();
}
});
please check https://jsfiddle.net/Shilpi/uhfruo1a/2/

Changing the function of a button in a website

So below I have some code of what I'm working with. Right now, if you just launch the website, the #picture div has a background image, and if you press any of the buttons, that picture is replaced with something else.
So what I can't figure out is how the buttons would change what they do after pressing a button. Let's say you click on Button 1 and the background image is replaced. I want the buttons to recognize what background image is in that div and change functions accordingly, in my case, I want them to change what pictures they replace the current one with.
If the current background of #picture is X, you have ABC choices, if the background of #picture is Y, you have DEF choices, would be a TLDR explanation maybe.
<div id="adventure">
<div class="picture">
</div>
<div id="choice">
<button class="button1">Val 1</button>
<button class="button2">Val 2</button>
<button class="button3">Val 3</button>
</div>
</div>
$('.button1').click(function() {
$('.picture').css('background-image',
'url("image1")'
);
});
$('.button2').click(function() {
$('.picture').css('background-image',
'url("image2")'
);
});
$('.button3').click(function() {
$('.picture').css('background-image',
'url("image3")'
);
});
I've probably gone about doing this in a bad way but I'm really at a loss on how I would do this. I can only think up of one way of doing it and that is to have a bunch of if statements depending on what background is in the #picture div but I don't know how to implement that.
This demo relies on the class .active which determines which set of buttons (called .group) are visible, whilst the other .groups remain absent. The #switch button will toggle through each group.
You must name your images according to the id of the button it belongs to.
Example:
HTML of Button
<button id="image_of_sky.png">D</button>
URL to Image
http://domain.com/path/to/image_of_sky.png
jQuery img variable
var img = "http://domain.com/path/to/"+btn;
Snippet
$('.button').click(function() {
var btn = $(this).attr('id');
var img = "https://placehold.it/330x150?text=" + btn;
$('.picture').css('background-image',
'url(' + img + ')'
);
});
$('#switch').click(function() {
var act = $('.group.active');
var next = act.next();
act.removeClass('active');
next.addClass('active');
if (act.attr('id') == "choiceGHI") {
$('#choiceABC').addClass('active');
}
});
#adventure {
width: 395px;
}
.picture {
width: 330px;
height: 150px;
border: 1px outset grey;
}
.group {
width: 330px;
display: none;
padding: 0;
}
.button {
width: 32.5%;
margin: 0;
}
.active {
display: block;
}
#switch {
float: right;
margin: -20px 0 0 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="adventure">
<div class="picture"></div>
<div id="choiceABC" class="group active">
<button id="Image1" class="button">A</button>
<button id="Image2" class="button">B</button>
<button id="Image3" class="button">C</button>
</div>
<div id="choiceDEF" class="group">
<button id="Image4" class="button">D</button>
<button id="Image5" class="button">E</button>
<button id="Image6" class="button">F</button>
</div>
<div id="choiceGHI" class="group">
<button id="Image7" class="button">G</button>
<button id="Image8" class="button">H</button>
<button id="Image9" class="button">I</button>
</div>
<button id="switch">Switch</button>
</div>

Categories