Change div style on button click within loop - javascript

So i'm using a While loop in PHP to return results from my database. Basically images with a button on each, when that button is clicked I want to display the mapOverlay div which will have a map on it. How do I select only the div that relates to the button i'm clicking? I can get it to open them all or just the first one but not the exact one with the button i'm click on.
This code sits within echo tags but i've removed to ease of reading:
<div class="resultContainer">
<div class="mapOverlay animated slideInLeft"></div>
<div class="resultsInfo">
<div class='resultsInfo_user'>
<div class='results_dp' style='background-image:url($row[dp])'></div>
<p style='color:#565656;padding: 0px 10px 0px 10px;float: left;font-weight:600'>ucfirst($row[firstName])</p>
</div>
</div>
<div class='resultsImage' style='background-image: url($row[image])'>
<div class='resultsImage_overlay'>
<div class='resultsImage_overlay_info'>
<p style='font-size:1.1em;font-weight:600;color:#4A4A4A;margin:0'>$row[title]</p>
<p style='margin: 5px 0px 1px 0px;font-size:0.9em;color:#828282'>$distanceMiles</p>
<p style='margin:0;font-size:0.9em;color:#828282'>$row[expiry]</p>
</div>
<div class='resultsImage_overlay_button'>
<button class='button-green openMap'>Get Directions</button>
</div>
</div>
</div>
</div>
Here's the javascript I've got:
var button = document.getElementsByClassName("openMap")
for(var i = 0; i < button.length; i++){
button[i].addEventListener("click", function(){
var overlay = document.getElementsByClassName("mapOverlay")
overlay.style.display = "block"
})
}
I'm going to toggle a class when I get it working but just using style.display to get it working first for ease of testing. I also want resultsImage_overlay to display above the overlay so the button remains visible / able to close the div overlay again.
I'm looking to be pointed in the right direction here, complete solutions welcome but I appreciate I haven't provided all the blocks!

You can try with closest() and previousElementSibling on this object.
Demo:
var button = document.getElementsByClassName("openMap")
for(var i = 0; i < button.length; i++){
button[i].addEventListener("click", function(){
var overlay = this.closest('.resultsImage').previousElementSibling.previousElementSibling;
overlay.style.display = "block"
})
}
.mapOverlay {display: none}
<div class="resultContainer">
<div class="mapOverlay animated slideInLeft">mapOverlay 1</div>
<div class="resultsInfo">
<div class='resultsInfo_user'>
<div class='results_dp' style='background-image:url($row[dp])'></div>
<p style='color:#565656;padding: 0px 10px 0px 10px;float: left;font-weight:600'>John</p>
</div>
</div>
<div class='resultsImage' style='background-image: url($row[image])'>
<div class='resultsImage_overlay'>
<div class='resultsImage_overlay_info'>
<p style='font-size:1.1em;font-weight:600;color:#4A4A4A;margin:0'>Some Title</p>
<p style='margin: 5px 0px 1px 0px;font-size:0.9em;color:#828282'>50</p>
<p style='margin:0;font-size:0.9em;color:#828282'>12-12-2020</p>
</div>
<div class='resultsImage_overlay_button'>
<button class='button-green openMap'>Get Directions</button>
</div>
</div>
</div>
<div class="mapOverlay animated slideInLeft">mapOverlay 2</div>
<div class="resultsInfo">
<div class='resultsInfo_user'>
<div class='results_dp' style='background-image:url($row[dp])'></div>
<p style='color:#565656;padding: 0px 10px 0px 10px;float: left;font-weight:600'>Michael</p>
</div>
</div>
<div class='resultsImage' style='background-image: url($row[image])'>
<div class='resultsImage_overlay'>
<div class='resultsImage_overlay_info'>
<p style='font-size:1.1em;font-weight:600;color:#4A4A4A;margin:0'>Some Title 2</p>
<p style='margin: 5px 0px 1px 0px;font-size:0.9em;color:#828282'>40</p>
<p style='margin:0;font-size:0.9em;color:#828282'>11-11-2020</p>
</div>
<div class='resultsImage_overlay_button'>
<button class='button-green openMap'>Get Directions</button>
</div>
</div>
</div>
</div>

Related

Change the Page layout when a button is clicked

I want to change the layout of a page that has 3 columns:
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
... to 4 columns when a button is clicked:
<div>
<div class="container">
<div class="row" >
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
<div class="col-md-3"></div>
</div>
</div>
</div>
I have no clue on how to do this.
There are many ways you can add another div. Here is my approach :
function appendDiv(){
let row = document.getElementsByClassName('row');
// change className for all the col-md-4 div
document.querySelectorAll('.col-md-4').forEach(function(item) {
item.className = 'col-md-3';
})
//create new div;
let col = document.createElement('div');
// add classname to div
col.className = "col-md-3"
row[0].appendChild(col)
}
.col-md-4{
border : 1px solid blue;
height : 20px;
}
.col-md-3{
border : 1px solid green;
height : 20px;
}
<div>
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
<button onClick='appendDiv()'>click</button>
</div>
</div>
There's a few ways this could be done depending on your data, however, here's one angle.
If you have both your 4 column & 3 column versions of the data loaded on the page (but one hidden with css). You could run something like this.
HTML
<div id="colsThree" class="displayArea show">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<div id="colsFour" class="displayArea">
<div class="container">
<div class="row" >
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
<button id="changeColumns">Click Me To Change Columns</button>
Javascript
const buttonEl = document.querySelector("#changeColumns");
buttonEl.addEventListener('click', () => {
const outputEls = document.querySelectorAll('.displayArea')
outputEls.forEach((outputEl) => {
outputEl.toggle("show")
})
});
CSS
.displayArea {
display: none;
}
.displayArea.show {
display: block;
}
Use forEach and appendChild method.
const btn = document.querySelector('#btn')
btn.onclick = function() {
const targetClasses = document.querySelectorAll('.col-md-4')
targetClasses.forEach((tag, idx) => {
tag.className = 'col-md-3'
const lastIdx = targetClasses.length - 1
if (idx === lastIdx) {
const tag = document.createElement('div')
, row = document.querySelector('.row')
tag.className = 'col-md-3'
tag.innerText = '4'
row.appendChild(tag)
}
})
console.log(targetClasses)
return
}
<div>
<button id="btn">Click me</button>
<div class="container">
<div class="row" >
<div class="col-md-4">1</div>
<div class="col-md-4">2</div>
<div class="col-md-4">3</div>
</div>
</div>
</div>
If you're only using vanilla HTML, CSS, and JavaScript, then one of the ways to achieve this is by adding a click listener to the button beforehand. FYI: for brevity's sake, I'll call the div element with row class as parent. When user clicks the button, then it should
remove col-md-4 class and add col-md-3 class to all the children elements of parent.
add a new div element with col-md-3 class into parent.
Here's a link to the codepen for your reference.
const button = document.querySelector('button');
const rowDiv = document.querySelector('.row');
button.addEventListener('click', (e) => {
Array.from(rowDiv.children).forEach(childDiv => {
childDiv.classList.remove('col-md-4');
childDiv.classList.add('col-md-3');
});
const newDiv = document.createElement('div');
newDiv.classList.add('col-md-3');
rowDiv.appendChild(newDiv);
// I disabled the button to prevent the user
// from clicking it the second time.
e.target.disabled = true;
});
.button-parent {
margin: 15px 0;
}
.row {
height: 100vh;
}
.row > div:nth-child(1) {
background: red;
}
.row > div:nth-child(2) {
background: blue;
}
.row > div:nth-child(3) {
background: yellow;
}
.row > div:nth-child(4) {
background: green;
}
<head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
<div>
<div class="container">
<div class="button-parent">
<button class="btn btn-primary">Add div</button>
</div>
<div class="row">
<div class="col-md-4"></div>
<div class="col-md-4"></div>
<div class="col-md-4"></div>
</div>
</div>
</div>
</body>

Get next element with class (that's not a child or sibling)

With the press of a button, I want to toggle the class .active on the next div.bottom. These are basically accordions, but with a different structure.
Using nextElementSibling I guess won't work here to select the target element. How would one select such an element, that's neither a child nor a sibling (in plain JS)?
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button></button></div>
</div>
</div>
<div class="bottom"></div>
</div>
I'd do it by using closest to go up to the container .wrapper element, then querySelector to find the bottom element:
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button && button.closest(".wrapper");
const bottom = wrapper && wrapper.querySelector(".bottom");
if (bottom) {
bottom.classList.toggle("active");
}
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
Or the same thing using optional chaining (relatively new):
function onClick(event) {
const wrapper = event.target.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
}
Live Example:
// I've added event delegation here
document.body.addEventListener("click", function onClick(event) {
const button = event.target.closest(".inner button");
const wrapper = button?.closest(".wrapper");
const bottom = wrapper?.querySelector(".bottom");
bottom?.classList.toggle("active");
});
.active {
color: blue;
border: 1px solid black;
}
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button A</button></div>
</div>
</div>
<div class="bottom">Bottom A</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<div><button>Button B</button></div>
</div>
</div>
<div class="bottom">Bottom B</div>
</div>
By using closest() you can traverse the DOM upwards. With this it's easy to just get the relevant .bottom and toggle the active class on this element.
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', (e) => {
e.currentTarget.closest('.wrapper').querySelector('.bottom').classList.toggle('active');
});
});
.bottom {
display: none
}
.bottom.active {
display: block
}
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle</button>
</div>
</div>
<div class="bottom">Hidden content</div>
</div>
<div class="wrapper">
<div class="top">
<div class="inner">
<button type="button">Toggle 2</button>
</div>
</div>
<div class="bottom">Hidden content 2</div>
</div>

How to select the first element on a page using a particular class when each subsequent element using the same class is also a first child?

I'm making a Fibonacci spiral. Code below. I need to create a second layer of the spiral (a spiral is made with a series of nested divs) and am creating multiple layers using the for loop.
I need to change the height of the first div element in the file with class .item-one, no subsequent divs with class .item-one.
How do I select just the first one, bearing in mind there will be a further 4 div elements with class .item-one which are also all the first child of their parents, using just CSS and vanilla JS?
(I need to change the height of just the first one and need all the rest of the .item-one divs to be a different height.)
I've tried :first-child and :first-of-type but all .item-one elements are the first children of their respective parents, so both :first-child and :first-of-type select all of the .item-one elements, not just the first on the page.
This post select first element of the same class is close, except the solution selects all first child elements, whereas I only want to select the first div in the file with that class.
Code:
HTML
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four"></div>
</div>
</div>
</div>
JS
window.addEventListener('load', function() {
var $items = document.getElementsByTagName("div");
var $item = $items[0];
var $cloned_item = $item.cloneNode(true);
var $final_item = $items[$items.length - 1];
for (var i = 1; i <= 5; i++) {
$final_item.appendChild($cloned_item);
$final_item = $items[$items.length - 1];
$cloned_item = $item.cloneNode(true);
}
});
The end result looks something like this.
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">...and so on...</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
You'd have to use the parent to be able to single out the first child element. I used body in this case, but if you have a different parent then you'd use that.
body > .item-one {
border: 1px solid red;
}
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">...and so on...</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Or better would be to just give that outer div another class and use that if that's an option.
Does that what you are looking for?
div {border: 1px solid blue;}
.item-one {border: 1px solid yellow;}
body > .item-one {border: 2px solid red;}
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">
<div class="item-one">
<div class="item-two">
<div class="item-three">
<div class="item-four">...and so on...</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Edit after #LGSon open eyes comments.
You can use the > selector to apply the styling to the immediate first child of its parent with the .item-one class. If you don't have a parent for all of those elements, then using the body should do.
body>.item-one {
/* Styling here */
padding: 5px;
margin: 5px;
border: 5px black solid;
}

Wrap two different elements in the same div to all classes with jQuery?

I want the following:
<div class="contents"></div>
<div class="contents"></div>
<div class="contents">
<span><h3></h3></span>
<span><img></span>
<span><p></span>
</div>
To look like this:
<div class="contents">
<div>
<span><h3></h3></span>
<span><img></span>
</div>
<span><p></span>
</div>
for each of the contents classes. My code:
var contents = $('.page').find('.contents');
var nestedContents = contents.find('span:nth-child(1)');
nestedContents.attr("id", 'h3-location');
var imgContents = contents.find('span:nth-child(2)');
imgContents.attr("id", 'img-location');
$('#img-location, #h3-location').wrap('<div class="groupedContent"></div>');
But all this is doing is wrapping the img and h3 spans in separate groupedContent divs. I have tried wrapAll and it takes ALL the img and h3 spans in the document and puts them in the one groupedContent div in the FIRST contents class. I can't put the img and h3 spans inside the one groupedContent div within each contents class.
Here's a JSFiddle... https://jsfiddle.net/h2awu27c/2/
With HTML code like so...
<div class="contents"></div>
<div class="contents"></div>
<div class="contents">
<span><h3 id="h3-location">The H3</h3></span>
<span><img id="img-location" /></span>
<span><p>asd</p></span>
</div>
<div class="contents">
<span><h3 id="h3-location">The H3 2</h3></span>
<span><img id="img-location" /></span>
<span><p>asd 2</p></span>
</div>
<div class="contents"></div>
To achieve with JS / Jquery, i.e. remove empty .contents divs and wrap the children h3 and img tag inside a groupedContent div:
<div class="contents">
<div class="groupedContent">
<span><h3 id="h3-location">The H3</h3></span>
<span><img id="img-location" /></span>
</div>
<span><p>asd</p></span>
</div>
<div class="contents">
<div class="groupedContent">
<span><h3 id="h3-location">The H3 2</h3></span>
<span><img id="img-location" /></span>
</div>
<span><p>asd 2</p></span>
</div>
Use:
$('.contents').each(function() {
var $contents = $(this);
if ($contents.is(':empty')) {
$contents.remove();
}
else
{
$contents.children('span:has(h3),span:has(img)').wrapAll('<div class="groupedContent"></div>');
}
})
You can use .wrapAll() like below
$('.page .contents').each(function() {
var $contents = $(this);
$contents.children('span:has(h3),span:has(img)').wrapAll('<div class="groupedContent"></div>')
})
.contents {
border: 1px solid red;
padding: 5px;
}
.groupedContent {
border: 1px solid green;
padding: 5px;
}
span {
border: 1px solid blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="page">
<div class="contents">
<span><h3></h3></span>
<span><img src=""/></span>
<span><p>some p</p>?</span>
</div>
<div class="contents">
<span><h3></h3></span>
<span><img src=""/></span>
<span><p>some p</p>?</span>
</div>
<div class="contents">
<span><h3></h3></span>
<span><img src=""/></span>
<span><p>some p</p>?</span>
</div>
</div>
Note: Your html is invalid, span can't have p as its descendant
Use WrapAll() for the div.content rather than .Wrap(), then select the h3 and img parent span tags again and wrapAll those:
$('div.contents span:nth-child(1),div.contents span:nth-child(2)').wrapAll('<div></div>')
https://jsfiddle.net/x9asccrb/
If I understood your request well, you need this:
JS:
var page = $('.page'),
$contents = page.find('.contents:has(span)').contents(),
item = $contents.wrapAll('<div class="groupedContent"></div>');
$('.groupedContent').clone().appendTo(page.find('.contents:empty'));
HTML:
<div class="page">
<div class="contents"></div>
<div class="contents"></div>
<div class="contents">
<span><h3>H3</h3></span>
<span><img src="#"/></span>
<span><p>P</p></span>
</div>
</div>
So you take a content of a div which :has a span, and wrapping it in another div.groupedContent. Then you are cloning this wrapped content and appending it to the rest of .contents elements (:empty divs).
JSFiddle
Hope this helps

Edit css of "item" when clicking on corresponding "btn"

So I have this
<div class="btns">
<div class="btn1"></div>
<div class="btn2"></div>
<div class="btn3"></div>
<div class="btn4"></div>
</div>
<div class="prevs">
<div class="pre1"></div>
<div class="pre2"></div>
<div class="pre3"></div>
<div class="pre4"></div>
</div>
http://jsfiddle.net/uzpxjukv/
You have btn1, btn2, btn3 and btn4. I'm trying to make it so that when you press btn1, the div with the class pre1 should then get "display: block;" or something to make it visible. Then when btn2 is clicked, pre1 turns invisible again and pre2 turns visible.
Maybe something like this? If there will be more buttons, it should be more optimalized.
$('.btns').find('div').click(function(){
$('.prevs').find('div').eq($(this).index()).toggle();
});
$('.btns').find('div').click(function(){
$('.prevs').find('div').eq($(this).index()).toggle();
});
.prevs div:not(.pre1) {
display:none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btns">
<div class="btn1">Button 1</div>
<div class="btn2">Button 2</div>
<div class="btn3">Button 3</div>
<div class="btn4">Button 4</div>
</div>
<div class="prevs">
<div class="pre1">Previews 1</div>
<div class="pre2">Previews 2</div>
<div class="pre3">Previews 3</div>
<div class="pre4">Previews 4</div>
</div>
JSFIDDLE DEMO -> http://jsfiddle.net/uzpxjukv/5/
$('.btns div').click(function() {
var classNumber = this.className.slice(-1);
$('.prevs div').hide();
$('.pre' + classNumber).show();
});
On click of the button div, first hide all the pre divs and then show only the relevant div.
Try it
$('.btns > div').on('click', function() {
var numberOfDiv = $(this).attr('class').slice('-1'),
prevs = $('.prevs');
prevs.find('> div').hide();
prevs.find('.pre' + numberOfDiv).show();
});
This example is with your html code, if is possible to change it, you can get a better code.
See the fiddle
I have changed your HTML a little bit..Changed the class attribute of the prevs divsti ids.
HTML
<div class="btns">
<div class="btn1" id="1" onClick="reply_click(this.id)"></div>
<div class="btn2" id="2" onClick="reply_click(this.id)"></div>
<div class="btn3" id="3" onClick="reply_click(this.id)"></div>
<div class="btn4" id="4" onClick="reply_click(this.id)"></div>
</div>
<div class="prevs">
<div id="pre1"></div>
<div id="pre2"></div>
<div id="pre3"></div>
<div id="pre4"></div>
</div>
JS
function reply_click(id) {
document.getElementById("pre" + id).style.display = "block";
}
Provided that you know what naming system the divs use, you could use something along these lines. (To see properly working, view using developer tool)
$('.btns div').on('click', function() {
var currClass = $(this).attr('class').slice(-1); //get end of number of div clicked
$('.prevs div').css('display', 'none'); //reset all divs to being hidden
$('.pre' + currClass).css('display', 'inline-block'); //show desired div
});
.btns div {
background-color: gray;
}
.btns div, .prevs div {
width: 2em;
height: 2em;
display: inline-block;
padding-right: 0.2em;
}
.prevs div {
background-color: red;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="btns">
<div class="btn1"></div>
<div class="btn2"></div>
<div class="btn3"></div>
<div class="btn4"></div>
</div>
<div class="prevs">
<div class="pre1"></div>
<div class="pre2"></div>
<div class="pre3"></div>
<div class="pre4"></div>
</div>

Categories

Resources