I am looking to add multiple 'see more' buttons through out my page. At the moment when when I add a new one only the first button works and it breaks the second. So the first button works fine but when I've tried to copy this thumbnail over and make another one with the same see more details. The second button only changes the first thumbnails 'see more'
function toggle() {
let Text = document.getElementById('moreDetails');
if (Text.style.display == "none") {
Text.style.display = "block";
} else {
Text.style.display = "none";
}
}
document.getElementById("moreDetails").style.display = "none";
<div id="thumbnail-frame">
<div id="thumbnail" <div id="details">
<div id="moreDetails">
<h3> 001 </h3>
<h3> Saturate Radio </h3>
<h4> N00DS </h4>
</div>
<button title="Click to Show" type="button" onclick="toggle()">More Details</button>
</div>
</div>
<div id="thumbnail-frame">
<div id="thumbnail" <div id="details">
<div id="moreDetails">
<h3> 002 </h3>
<h3> Saturate Radio </h3>
<h4> N00DS </h4>
</div>
<button title="Click to Show" type="button" onclick="toggle()">More Details</button>
</div>
</div>
Main problem is that you can't have duplicate IDs on a page. Using classes works ok, or using relative position of your html elements.
function toggle(button){
// this works because the button is immediately after the "moreDetails" element it pertains to
let Text = button.previousElementSibling;
// this would work if you move the button so it is not immediately after moreDetails, but still in the same parent div.
//let Text = button.parentElement.querySelector(".moreDetails");
if(Text.style.display == "none"){
Text.style.display= "block";
}
else {
Text.style.display = "none";
}
}
const moreDetailses = document.querySelectorAll(".moreDetails");
for (let i = 0; i < moreDetailses.length; i++) {
moreDetailses[i].style.display = "none";
}
<div class="details">
<div class="moreDetails">
<h3> 001 </h3>
<h3> Saturate Radio </h3>
<h4> N00DS </h4>
</div>
<button title="Click to Show" type="button" onclick="toggle(this)">More Details</button>
</div>
<div class="details">
<div class="moreDetails">
<h3> 002 </h3>
<h3> Saturate Radio </h3>
<h4> N00DS </h4>
</div>
<button title="Click to Show" type="button" onclick="toggle(this)">More Details</button>
</div>
An id is only allowed to be used once per page, so that your toggle script will not work as expected when you have multiple elements with the same id.
To make it functional, and keep the required changes to a minimum, you should do the following:
switch id to class so the HTML is valid
allow your toggle button to pass along itself, for example onclick="toggle(this)"
move through the dom to get to the element you want to toggle (parentNode, firstChild etc.)
Related
I would like to make 3 buttons with each one make all the content div to display: none and depending on the button you have click one of the content div change to display: block. For example, If I click on the second button It will show only the second div content.
function showPanel(id) {
var elements = document.getElementsByClassName("content");
for (let i = 0; i < elements.length; i++) {
document.getElementById(i).style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<div class="content">
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
There's a couple of issues in your code. Firstly length is a property, not a method, so you don't need the () suffix to invoke it. Secondly, there's no className attribute in HTML. This should just be class. Lastly the parent container shares the same class as the elements you're hiding, so all the child elements get hidden, even if they have display: block applied to them.
With these issues corrected, your code would look like this:
function showPanel(id) {
var elements = document.getElementsByClassName("panel");
for (let i = 0; i < elements.length; i++) {
elements[i].style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('p1')">test1</button>
<button onclick="showPanel('p2')">test2</button>
<button onclick="showPanel('p3')">test3</button>
<div class="content">
<div id="p1" class="panel">
<p>TEST1</p>
</div>
<div id="p2" class="panel">
<p class="other">TEST2</p>
</div>
<div id="p3" class="panel">
<p class="other">TEST3</p>
</div>
</div>
However it's worth noting that using onX attributes is outdated and not good practice. A better solution would be to use unobtrusive event handlers and provide custom metadata to the event handler through data attributes placed on the elements.
The improved version of the logic would look like this:
let buttons = document.querySelectorAll('button');
let panels = document.querySelectorAll('.panel');
buttons.forEach(button => {
button.addEventListener('click', e => {
panels.forEach(panel => {
panel.style.display = panel.id === e.target.dataset.panel ? 'block' : 'none';
});
});
});
<button data-panel="1">test1</button>
<button data-panel="2">test2</button>
<button data-panel="3">test3</button>
<div class="content">
<div id="1" class="panel">
<p>TEST1</p>
</div>
<div id="2" class="panel">
<p class="other">TEST2</p>
</div>
<div id="3" class="panel">
<p class="other">TEST3</p>
</div>
</div>
No need for JS or Jquery. Instead of a button you can use an anchor tag. Then you calling with the anchor the id of the element. Last but not least you make the boxes hidden through CSS and use the :target selector to display the elements:
.content {
display: none;
}
.content:target {
display: block;
}
test1<br>
test2<br>
test3<br>
<div class="content-container">
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
Multiple issues.
Length can be calculated using elements.length and not elements.length()
You have given same class name to both the parent and the child divs. So hiding all elements with class name content will hide your whole parents itself. So after updating style.display = "block" to the required target, it will not work. Because your parent is already style.display = "none". So you should make a logic update there. So I changed the parent class name.
function showPanel(id) {
var elements = document.getElementsByClassName("content");
for (let i = 0; i < elements.length; i++) {
elements[i].style.display = "none";
}
document.getElementById(id).style.display = "block";
}
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<div>
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p class="other">TEST2</p>
</div>
<div id="3" class="content ">
<p class="other">TEST3</p>
</div>
</div>
A more elegant way I might approach a prob,problem like this would be to tie the panels and their triggers together using data-attributes. This way, you don't risk conflicts with other IDs that m ay be the same on the page (IDs should always be unique).
Before setting up my event listener, I would initialize an openPanel variable and set it to any panel that is already created with the active class name. Whenever we open a new panel, we will overwrite this variable vaklue, so we don't need to do a new querySelctor each time.
Then, in the CSS, rather than hiding all panels and then showing the one with the active class, we can write a single style that hides any panels without the active class using the :not negation selector.
This is how that would look (initializing this with panel #1 open by default, but you can simply remove the active class from it in the HTML if you don't want that):
let openPanel = document.querySelector('[data-panel-id].active');
document.addEventListener('click', e => {
if (e.target?.matches?.('[data-panel-target]')) {
const id = e.target.dataset.panelTarget;
if (id) {
const panel = document.querySelector(`[data-panel-id="${id}"]`);
if (panel) {
openPanel?.classList.remove('active');
panel.classList.add('active');
openPanel = panel;
}
}
}
})
[data-panel-id]:not(.active) {
display: none;
}
<button data-panel-target="1">test1</button>
<button data-panel-target="2">test2</button>
<button data-panel-target="3">test3</button>
<main>
<div data-panel-id="1" class="active">
<p>TEST #1</p>
</div>
<div data-panel-id="2">
<p>TEST #2</p>
</div>
<div data-panel-id="3">
<p>TEST #3</p>
</div>
</main>
I already submitted a separate solution with my preferred recommendation, but I wanted to provide an answer to your question using the same approach you started with so as not to deviate from the code you already have in place.
The code you already had in place was actually fairly close to working already. The main issue I saw was that you were using document.getElementById(i) where you should actually have been using elements[i]. We can improve this further though, by replacing the for loop with a for..of loop, and determining inline whether the current element being evaluated is the one we want to show. If so, we use 'block', otherwise 'none'.
After initializing our function, we can call it on one of our IDs within the JS to have one panel open by default. **It's also important that the parent of all these .content elements NOT contain the class name content as well, as that would conflict with your function. I have replaced that parent element with a simple <main>…</main> element.
Here is how I would achieve solving this using your existing approach:
function showPanel(contentId) {
const elements = Array.from(document.getElementsByClassName('content'));
for (const element of elements) {
element.style.display = element.id === contentId ? 'block' : 'none';
}
}
showPanel('1');
<button onclick="showPanel('1')">test1</button>
<button onclick="showPanel('2')">test2</button>
<button onclick="showPanel('3')">test3</button>
<main>
<div id="1" class="content">
<p>TEST1</p>
</div>
<div id="2" class="content">
<p>TEST2</p>
</div>
<div id="3" class="content ">
<p>TEST3</p>
</div>
</main>
I am trying to do logic using javascript, so that if div where class is b-format if innerhtml value is Audio it will hide cart-button div, else it will hide the more-button div. For some reason its not working.
var itemButtons = document.querySelectorAll('.slider-wrapper');
for(var i=0; i<itemButtons.length; i++){
var b_format_element = itemButtons[i].getElementsByClassName("b-format")[0];
var cart_element = itemButtons[i].getElementsByClassName("cart-button")[0];
var more_element = itemButtons[i].getElementsByClassName("more-button")[0];
if(b_format_element.innerHTML == "Audio"){
cart_element.style.display = "none";
} else {
more_element.style.display = "none";
}
}
this is html code
<div class="slider-wrapper">
${#Recommend} // this repeat record
<a class="product" href="#">
<div>
<p class="b-format">Audio</p>
</div>
<div class="product-items cart-button">
<span>Read more</span>
</div>
<div class="product actions product-items more-button">
<span>Read more</span>
</div>
</a>
${/Recommend}
</div>
Not good idea to use the same ID tags over and over in a loop. Instead, use a class name. Also, using querySelector will get you the first matching element. It also looks like you want to cycle through the inner DIVs of the slider-container, rather than cycling through multiple slider containers. I added an inner container .record.
document.querySelectorAll('.slider-wrapper .record').forEach(c => {
let isAudio = c.querySelector('.b-format')?.innerText.trim() === 'Audio';
c.querySelector('.cart-button').style.display = isAudio ? 'none' : 'block';
c.querySelector('.more-button').style.display = !isAudio ? 'none' : 'block';
})
.cart-button {
color: #f00;
}
.more-button {
color: #999;
}
<div class="slider-wrapper">
<div class='record'>
<div>
<p class="b-format">Audio</p>
</div>
<!-- buttom -->
<div class="cart-button">
<span>Les mer</span>
</div>
<div class="more-button">
<span>Les mer</span>
</div>
<!-- button end -->
</div>
<div class='record'>
<div>
<p class="b-format">Not Audio</p>
</div>
<!-- buttom -->
<div class="cart-button">
<span>Les mer</span>
</div>
<div class="more-button">
<span>Les mer</span>
</div>
<!-- button end -->
</div>
</div>
I'm completely new to JavaScript and struggling to get this working. I'm trying to display contact details in an alert box when a button is clicked.
The information is displayed in nested DIVs outlined below:
<div class="info-and-actions">
<div class="info">
<div class="name-container">
<h4 class="name">Trader 1</h4>
</div>
<div class="details-container">
<p class="tele">###############</p>
<p class="email">hello#url.com</p>
</div>
<div class="actions">
<button class="displayContactDetails">Display contact details</button>
</div>
</div>
</div>
<div class="info-and-actions">
<div class="info">
<div class="name-container">
<h4 class="name">Trader 2</h4>
</div>
<div class="details-container">
<p class="tele">###############</p>
<p class="email">hello#url.com</p>
</div>
<div class="actions">
<button class="displayContactDetails">Display contact details</button>
</div>
I am using this JavaScript with an event listener on the displayContactDetails button to display the contact details in the alert box.
function displayContactDetails() {
var contactName = document.querySelector("a.name-link.profile-link").textContent;
var contactTele = document.querySelector("p.tele").textContent;
alert("Contact " + contactName + " on " + contactTele);
}
This currently displays the first trader's contact details when any displayContactDetails button is clicked.
What I want to do is select the relevant contact information from the parent elements of the button (.name-container and .details-container) - so when the second button is clicked, Trader 2's details are displayed.
How do I traverse the DOM to achieve this?
Your event will have the necessary info to locate the corresponding telephone element.
function displayContactDetails(e) {
var contactName = e. currentTarget // this is the element that has the click listener
.parentNode // this would be .actions div
.parentNode // this would be .info div
.querySelector('.name a') // the link with the name
.innerHTML; // the inner contents of the link, in this case the name
// repeat but change querySelector to grab the telephone
alert("Contact " + contactName + " on " + contactTele);
}
or easier with jquery
$('.displayContactDetails').on('click', function(e){
var name = $(this)
.closest('.info') // goes up in the tree until .info
.find('.name').html(); // then back to .name and get content
alert(name);
});
With jquery it is not only easier, but it is also more resilient to changes in the html structure. Jquery will go up the DOM tree until it finds the .info div. With plain js you have to hardcode the number of times to jump to the parent element, or make a loop for it.
You can also try the other answers suggested but you also have to maintain the additional id's and markup required for it to work.
i hope passing a unique info on the function call
function displayContactDetails(id){
var contactName = document.getElementById("name-link-"+id).textContent;
alert("Contact " + contactName );
}
<div class="info-and-actions">
<div class="info">
<div class="name-container">
<h4 class="name">Trader 1</h4>
</div>
<div class="details-container">
<p class="tele">###############</p>
<p class="email">hello#url.com</p>
</div>
<div class="actions">
<button class="displayContactDetails" onclick="displayContactDetails('one')">Display contact details</button>
</div>
</div>
</div>
<div class="info-and-actions">
<div class="info">
<div class="name-container">
<h4 class="name">Trader 2</h4>
</div>
<div class="details-container">
<p class="tele">###############</p>
<p class="email">hello#url.com</p>
</div>
<div class="actions">
<button class="displayContactDetails" onclick="displayContactDetails('two')">Display contact details</button>
</div>
i have a problem with showing popout window. I have 2 popout windows. They are generated through php foreach (in echo)... The problem is, every time is gonna show only the FIRST span on the page, but that SECOND doesn't work. PHP code here:
echo __( "<div class='action' style='margin-bottom: -20px'>
<div id='box' style='background-color:". $active_row->color .";width: 10px;height:10px;float:left;margin:5px 10px 0px 10px;'> </div>
<span id='myBtn' style='color:orange'> ". $active_row->title ."<span id='GoogleADD' style='float:right; color:orange; text-decoration:underline'> Add </span> </span> <span id='end' style='float:right; margin-right: 10px'>". $endDateString ."</span> <span style='float:right'> - </span> <span id='start' style='float:right; margin-left:10px'> ". $newDateString ." </div> <!-- Trigger/Open The Modal -->
<!-- The Modal -->
<div id='myModal' class='modal'>
<!-- Modal content -->
<div class='modal-content'>
<div class='modal-header'>
<span class='close'>×</span>
<h2 style='text-align:center'>Podrobnosti o akci</h2>
</div>
<div class='modal-body'>
<p> Název akce: <b>". $active_row->title ."</b> </p>
<p> Podrobnosti: <b>". $active_row->description ." </b> </p>
<p> Začátek akce: <b>". $newDateString ." </b> </p>
<p> Konec akce: <b>". $endDateString ." </b> </p>
<p> Přidat akci do vašeho Google Kalendáře: <b style='color: orange; text-decoration: underline'> ADD ME! </b> </p>
</div>
<div class='modal-footer'></div>
</div>
</div>");
Then I have a script, where i want to show them, when someone click on them. I am checking the ID on "third" row here (span id="myBtn").
Here is my jQuery script.
<script>
// Get the modal
var modal = document.getElementById('myModal');
// Get the button that opens the modal
var btn = document.getElementById('myBtn');
// Get the <span> element that closes the modal
var span = document.getElementsByClassName('close')[0];
// When the user clicks the button, open the modal
btn.onclick = function() {
modal.style.display = 'block';
}
// When the user clicks on <span> (x), close the modal
span.onclick = function() {
modal.style.display = 'none';
}
// When the user clicks anywhere outside of the modal, close it
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = 'none';
}
}
</script>"
Can you help me out please ? Thanks a lot!
I think this will help you to achieve your goal. I have removed some unwanted details from your example, and try to keep it as simple as possible.
<div class='action' >
<span class="MyBtn" rel="myModal1" style='color:orange'>Title 1</span>
</div>
<!-- The Modal 1 -->
<div id='myModal1' class='modal' style="display:none">
<!-- Modal content 1 -->
<div class='modal-content'>
<div class='modal-header'>
<span class='close' rel="myModal1" >X</span>
<h2 style='text-align:center'>Popup 1 header</h2>
</div>
<div class='modal-body'>'Popup 1 content goes here...'</div>
<div class='modal-footer'></div>
</div>
</div>
<div class='action' >
<span class="MyBtn" rel="myModal2" style='color:orange'>Title 2</span>
</div>
<!-- The Modal 2 -->
<div id='myModal2' class='modal' style="display:none">
<!-- Modal content 2-->
<div class='modal-content'>
<div class='modal-header'>
<span class='close' rel="myModal2" >X</span>
<h2 style='text-align:center'>Popup 2 header</h2>
</div>
<div class='modal-body'>'Popup 2 content goes here...'</div>
<div class='modal-footer'></div>
</div>
</div>
The above html portion can be generated with the help of any looping structure. Please notice that I have write the id of the modal container in rel attribute at two different places.
In the span tag with MyBtn class.
In the span tag with close class.
You need to generate the unique id for your modal cotainer and also should be written there. (You can use $active_row->ID, insted of the serial number)
Here is the script, which will open and close the modal box.
<script type="text/javascript">
var button_click = function() {
var ModalID = this.getAttribute("rel");
document.getElementById(ModalID).style.display = 'block';
};
var close_click = function() {
var ModalID = this.getAttribute("rel");
document.getElementById(ModalID).style.display = 'none';
};
// Get the button that opens the modal
var btn = document.getElementsByClassName('MyBtn');
// Get the <span> element that closes the modal
var close = document.getElementsByClassName('close') ;
// Assign the events to the buttons (open & close)
for(iCount = 0; iCount < btn.length; iCount++) {
btn[iCount].addEventListener('click', button_click, false) ;
close[iCount].addEventListener('click', close_click, false) ;
}
</script>
I wish it will help enough.
Have a great time.
My html page content is below
<div>
<div>
Summary 1
</div>
<div style="display:none">
Details 1
</div>
<button>Read More</button>
</div>
Details content is collapsed initially. When user clicks the Read More button, I need to show details content. I can make it possible. i will define id for details div tag and javascript for onclick event of button. Using id i will change the div style display.
But i have multiple list of sections based on the back end data. so my page would be renderd like below
<div>
<div>
Summary 1
</div>
<div style="display:none">
Details 1
</div>
<button>Read More</button>
</div>
<div>
<div>
Summary 2
</div>
<div style="display:none">
Details 2
</div>
<button>Read More</button>
</div>
<div>
<div>
Summary 3
</div>
<div style="display:none">
Details 3
</div>
<button>Read More</button>
</div>
Now How can i acheive the expand and collapse functionality when Read More button is clicked.
Using plain javascript and with the strategic addition of some classes, you could do this which would make each button into a toggle that even changes it's text according to the toggle state. Then one piece of javascript would serve for all the repeated instances of this structure and the code would be independent of the exact HTML layout of the summary, details and button (as long as they retained the same classes and were in the same container div.
HTML:
<div>
<div>
Summary 1
</div>
<div class="details" style="display:none">
Details 1
</div>
<button class="readMore">Read More</button>
</div>
<div>
<div>
Summary 2
</div>
<div class="details" style="display:none">
Details 2
</div>
<button class="readMore">Read More</button>
</div>
<div>
<div>
Summary 3
</div>
<div class="details" style="display:none">
Details 3
</div>
<button class="readMore">Read More</button>
</div>
And the javascript:
function toggleVis(el) {
var vis = el.style.display != "none";
if (vis) {
el.style.display = "none";
} else {
el.style.display = "block";
}
return(!vis);
}
(function() {
var readMore = document.getElementsByClassName("readMore");
for (var i = 0; i < readMore.length; i++) {
readMore[i].onclick = function(e) {
var vis = toggleVis(e.target.parentNode.getElementsByClassName("details")[0]);
e.target.innerHTML = vis ? "Read Less" : "Read More";
}
}
})();
Working Demo: http://jsfiddle.net/jfriend00/aMBkJ/
Note: This would require a shim for getElementsByClassName() on older versions of IE.
By using jQuery .prev() you may achieve it easily.
$('button').on('click', function(e){
$(this).prev('div').toggle();
});
DEMO
You can try this with jquery
<button class="readmore">Read More</button>
jQuery
$('.readmore').click(function(){
$(this).prev().show();
});
Here it is how you do it using jquery:
The HTML code:
<div class="stuff">
<dl id="faq">
<dt>What shouldn't I do to the bird?</dt>
<dd>Never try to treat a fracture at home.</dd>
</div>
The JQuery code:
jQuery(document).ready(function() {
$('#faq').find('dd').hide().end().find('dt').click(function(){
$(this).next().slideToggle();
});
});