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();
});
});
Related
I have a few buttons in the HTML file code with Bulma similar like this:
<a class="button" data-target="A">Section A</a>
<a class="button" data-target="B">Section B</a>
<a class="button" data-target="C">Section C</a>
And there are a few section like this which can interact with the buttons:
<div id="A" class="contentswitch">
<article class="message">
<div class="message-header" id="h1">
<p>Section A-1</p>
</div>
<div class="message-body">
Message 1 of section A
</div>
<div class="message-header">
<p>Section A-2</p>
</div>
<div class="message-body">
Message 2 of section A
</div>
</article>
</div>
As I add code like this in the JS, it can add a class called "is-hidden" to all the div ejectment which contains "contentswitch" class.
$("a.button").on("click", function(){
$("div.contentswitch").addClass("is-hidden");
});
What can I do if I want to remove the class ("is-hidden") from specific div element, like if I click the button of Section A, then it add "is-hidden" class to all the div element contain content switch then remove it from the div element with the id "A"?
Thank you so much
You can use data-target to connect the clicked button to the div you want to show. And hide the rest of them.
Also, use button iso a tag. a tag has a specific purpose in HTML and should be used only in combination with href attribute.
const buttons = $('.button');
const contentSwitchDivs = $('.contentswitch');
buttons.on("click", function(){
const btnTarget = $(this).data('target')
const contentToShow = $(`#${btnTarget}`)
contentSwitchDivs.not(btnTarget).hide();
contentToShow.show();
});
.contentswitch {
display: none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="button" data-target="A">Section A</button>
<button class="button" data-target="B">Section B</button>
<button class="button" data-target="C">Section C</button>
<div id="A" class="contentswitch">
A contentswitch
</div>
<div id="B" class="contentswitch">
B contentswitch
</div>
<div id="C" class="contentswitch">
C contentswitch
</div>
i want to target the element above my show more button, so when i click the button more text appears i don't want to target it by class name or id
here is my code
<div class="ccontainer" id="ccontainer">
<p id="context"> content </p>
<div class="img" id="cntimgcon" >
<img src="images\image2.jpg" id="cntimgp1">
</div>
<p id="context"> content </p>
</div>
<Button id="showmore" onclick=" this.parentElement.style.maxHeight = 'none'"> show more </button>
Don't refer to the IDs that can get cumbersome. Instead give your show more button a class to refer to. That will give you the ability to add many to the same page without needing to adjust/track the IDs.
This is a basic example that toggles a class on the content div that will show the full div. Obviously the details are up to your specific needs.
Using previousElementSibling allows you to refer to the previous element.
document.addEventListener("click",function(e){
let btn = e.target;
if(btn.className.indexOf("showmore") > -1){
btn.previousElementSibling.classList.toggle("active");
}
});
.ccontainer{
height:50px;
overflow:hidden;
border:1px solid #000;
margin:10px 0;
padding:10px;
}
.ccontainer.active{
height:auto;
}
<div class="ccontainer">
<p id="context"> content </p>
<div class="img" id="cntimgcon" >
<img src="images\image2.jpg" id="cntimgp1">
</div>
<p id="context"> content </p>
</div>
<Button class="showmore"> show more </button>
<div class="ccontainer">
<p id="context"> content3 </p>
<div class="img" id="cntimgcon" >
<img src="images\image2.jpg" id="cntimgp1">
</div>
<p id="context"> content4 </p>
</div>
<Button class="showmore"> show more </button>
When using jQuery you can use:
$(this).prev(); // $(this) is equal to the button
// or very specific
$(this).prev(".container");
Using vanilla JS you could use something like
onclick="this.previousSibling"
not sure if vanilla js has a way of selecting a previous node by identifier.
Any whitespace or comment block is also considered a previousSibling, so be careful with that.
html
<div>
<div class="ccontainer" id="ccontainer">
<p id="context"> content </p>
<div class="img" id="cntimgcon" >
<img src="images\image2.jpg" id="cntimgp1">
</div>
<p id="context"> content </p>
</div>
<Button id="showmore" onclick="hideParent(this)"> show more </button>
</div>
js
function hideParent(elm){
console.log(elm.previousElementSibling.innerHTML )
}
see https://jsfiddle.net/rkqnmv0w/
If your using only vanila JS you can access the previous element with the previousElementSibling property.
Example:
var showMoreButton = document.getElementById('showmore');
var previousElement = showMoreButton.previousElementSibling;
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 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.)
I'm trying to hide a visible section then show a hidden section using JQuery .hide() and .show(). When the event fires (on clicking an image) it only hides the visible section momentarily, then becomes visible again with the previously hidden section now visible below the first visible section. Based on the docs I've read and some tutorials I've watched this shouldn't be happening.
HTML:
<div class="transition-div">
<section class="project-section">
<div class="project-wrapper" id="project-one">
<div class="desc-text">
<h2>Header</h2>
</div>
<div class="project-image-wrapper">
<img class="project-image" id="project-img-one" src="images/img1.png">
<button class="project-button">Button
</button>
</div>
</div>
</section>
<section class="project-section hidden">
<div class="project-wrapper">
<div class="desc-text">
<h2>Header</h2>
<p>Some description text</p>
</div>
<div class="project-image-wrapper">
<img class="project-image" src="images/img1.png">
</div>
</div>
</section>
</div>
JS:
$('.hidden').hide();
var slideSection = function() {
$('.project-image-wrapper').click(function() {
var $parentDiv = $(this).parents('.transition-div');
var $childToShow = $parentDiv.find('.hidden');
var $childToHide = $childToShow.siblings('.project-section');
$childToHide.hide(300, hideActiveSection());
function hideActiveSection() {
$childToHide.addClass('hidden');
$childToShow.show(300, function() {
$(this).removeClass('hidden');
});
}
});
};
slideSection();
How would I get the section I want to hide to do so persistently until I click the currently visible project image to show it? Could my CSS be interfering with what I want to do here? If so I'll post the code. Thanks!