Multiple show and hide buttons - javascript

I want my show more and hide button to work for multiple text contents, now it's only working for one of them. I have 12 different texts that I want to be able to show and hide with 12 different buttons, like the one in my code. How do I do this?
var content = document.getElementById("content");
var button = document.getElementById("show-more");
button.onclick = function() {
if (content.className == "open") {
//shrink the box
content.className = "";
button.innerHTML = "Läs mer";
} else {
//expand the box
content.className = "open";
button.innerHTML = "Dölj";
}
};
<div id="content">
Test
</div>
<a id="show-more">Läs mer</a>

Hej Linnéa!
IDs should be unique, and if you want multiple occurrences of something, you should use class names.
What I would do is to wrap the links inside of the container divs;
<div class="content">
Content
<a class="toggle" href="#">Läs mer</a>
</div>
<div class="content">
Content
<a class="toggle" href="#">Läs mer</a>
</div>
Then, instead of attaching your event listener to each anchor element, take advantage of event propagation, and add a listener to each content wrapper instead;
document.querySelectorAll('.content').forEach(function(contentDiv) {
contentDiv.onclick = function(e) {
if(e.target.classList.contains('toggle')) {
e.target.innerHTML = e.currentTarget.classList.contains('open') ? 'Dölj' : 'Läs mer';
e.currentTarget.classList.toggle('open');
}
}
});

It is better to use class than id. I have implemented simple snippet where you can design using just class names.
var content = document.getElementById("content");
$(".showHide").on("click", function() {
$(this).parent().find(".more").toggle();
if ($(this).parent().find(".more").is(":visible")) {
$(this).text("Show less");
} else {
$(this).text("Show more");
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="content">
<div class="text">
First text1
<div style="display:none;" class="more">Other text 1</div>
<a class="showHide" href="#">Show more</a>
</div>
<hr />
<div class="text">
First text2
<div style="display:none;" class="more">Other text 2</div>
<a class="showHide" href="#">Show more</a>
</div>
</div>

Use document querySelectorAll
var content = Array.from(document.querySelectorAll(".container"));
content.forEach(function(el){
//var content= el.querySelector(".content");
var button = el.querySelector(".show-more");
button.addEventListener("click", function () {
el.classList.toggle("open");
el.classList.contains("open") ? (button.innerHTML = "Dölj") : (button.innerHTML = "Läs mer");
}, false)
});
.container .content{display: none}
.container.open .content{display: block}
<section>
<article class="container">
<p>Lorem Ipsum is simply dummy</p>
<p class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<a class="show-more">Läs mer</a>
</article>
<article class="container">
<p>Lorem Ipsum is simply dummy</p>
<p class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<a class="show-more">Läs mer</a>
</article>
<article class="container">
<p>Lorem Ipsum is simply dummy</p>
<p class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<a class="show-more">Läs mer</a>
</article>
<article class="container">
<p>Lorem Ipsum is simply dummy</p>
<p class="content">Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</p>
<a class="show-more">Läs mer</a>
</article>
</section>

You can make it work for multiple paragraphs if you use classes for all elements, and then process it from there as element pairs (div + corresponding button).
I deliberately kept everything as much as possible the same as your original code, so it would be easy to understand the changes. And I added some 'hidden' content so you really see something happening.
var contentDivs = document.getElementsByClassName("content");
var buttons = document.getElementsByClassName("show-more");
for (var i = 0; i < contentDivs.length; i++) {
// "let" creates locally scoped variables for use in the function.
let div = contentDivs[i];
let button = buttons[i];
button.onclick = function() {
if (div.className == "open") {
//shrink the box
div.className = "content";
button.innerHTML = "Read more";
} else {
//expand the box
div.className = "open";
button.innerHTML = "Close";
}
};
}
div, a { font-size: 14px; }
.content { overflow: hidden; max-height: 18px }
<div class="content">
Div 1<br />.....<br />.....<br />.....
</div>
<a class="show-more">Read more</a>
<hr size="1">
<div class="content">
Div 2<br />.....<br />.....<br />.....
</div>
<a class="show-more">Read more</a>
<hr size="1">

Related

Add one div ans class to one specific div if multiple div have the same class? (Javascript)

In a class exercise, (studying Javascript), I need to add a div with the class q6 under an existing div for the question 6. Teacher made it harder(at least for me), as all questions have div with class question.
Is there a way to select a specific div if they all have the same class?
Here's an example of the html code (I translated the question, as it was in french):
<div id="content">
<div class="question">
<h2> Question 1. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q1"></div>
</div>
<div class="question">
<h2>Question 2. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q2"></div>
</div>
<div class="question q3">
<h2>Question 3. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question">
<h2>Question 4. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question q5">
<h2>Question 5. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question">
<h2>Question 6. <span>(2pts)</span></h2>
<h3>
Add a div with class <code>q6</code> at the end of the <code>h3</code> in this
question, in the <code><div class="question"></code>. If it's well
placed, an orange square will appear.
</h3>
<!-- You need to add the div here with javascript: <div class="q6"></div> -->
</div>
</div>
thanks for your answers
there are several ways to do it, here are some of them.
// fig 1
// use index
const questions = document.getElementsByClassName('question')
questions[5].innerHTML = 'fig 1 work'
// fig 2
// use data- prefix
// this should be the preferred way because this is easier to maintain
// when the list is dynamically created.
// also data- prefix is the least aggresive with web semantics
const target = document.querySelector(`.question[data-key='6']`)
if (target) {
target.innerHTML += 'fig2 work'
}
// fig 3
// use multiple classes
const targetAlt = document.querySelector('.question.q6')
if (targetAlt) {
target.innerHTML += 'fig3 work'
}
<div id="content">
<div class="question">
<h2> Question 1. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q1"></div>
</div>
<div class="question">
<h2>Question 2. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q2"></div>
</div>
<div class="question q3">
<h2>Question 3. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question">
<h2>Question 4. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question q5">
<h2>Question 5. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question q6" data-key="6">
<h2>Question 6. <span>(2pts)</span></h2>
<h3>
Add a div with class <code>q6</code> at the end of the <code>h3</code> in this question, in the <code><div class="question"></code>. If it's well placed, an orange square will appear.
</h3>
<!-- You need to add the div here with javascript: <div class="q6"></div> -->
</div>
</div>
One of the best method will be
Select all the nodes with class question with document.querySelectorAll
Parse the list of nodes and search of h2 tag.
Check whether that h2 tage consist of text Question 6.
If this condition satisfies then that will be the target node.
Create a dic using document.createElement("div") and assign the className as q6. Append this new node to the node that have been identfied on previous step.
Working Fiddle
function addDiv() {
const questionNodes = document.querySelectorAll('.question');
let searchNode;
questionNodes.forEach((node) => {
const questionNode = node.querySelector('h2');
if (questionNode.innerHTML.includes('Question 6.')){
searchNode = node;
}
});
if(searchNode) {
const newContent = document.createElement("div");
newContent.className = 'q6';
searchNode.appendChild(newContent);
}
}
.q6 {
width: 100%;
height: 200px;
background: brown;
}
<div id="content">
<div class="question">
<h2> Question 1. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q1"></div>
</div>
<div class="question">
<h2>Question 2. <span>(2pts)</span></h2>
Lorem ipsum
<div class="q2"></div>
</div>
<div class="question q3">
<h2>Question 3. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question">
<h2>Question 4. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question q5">
<h2>Question 5. <span>(3pts)</span></h2>
Lorem ipsum
</div>
<div class="question">
<h2>Question 6. <span>(2pts)</span></h2>
<h3>
Add a div with class <code>q6</code> at the end of the <code>h3</code> in this
question, in the <code><div class="question"></code>. If it's well
placed, an orange square will appear.
</h3>
<!-- You need to add the div here with javascript: <div class="q6"></div> -->
</div>
</div>
<button onclick="addDiv()">Add Div</button>

Manipulate content # for all div elements (with role="tabpanel") - style="display: none;" to display: block;

How to manipulate a website to change all
div elements (with role="tabpanel") - style="display: none;" to style="display: block;"
via class="accordionItemContent" could be possible as well
I would like to see the whole page/div elements with total content (so it doesn't matter is the manipulation is via JS or CSS .. or jquery)
<div class="accordionItemContent accordion ui-reset widget uibottom" role="tabpanel" style="display: none;"></div>
Because the page is behind a login probably a change via site inspect/console would be one way to go.
Update
I have been to fast when writing about "display"
if "block" it only shows a frame without content
I saw that there is another main difference in the element shown vs all other hidden once:
<h1 class="uiaccordion" role="tab" a-exp="false" a-sel="false" tabindex="-1"><a href="#" id="manage_content_11_ac" tabindex="-1"></div>
<h1 class="uiaccordion" role="tab" a-exp="true" a-sel="true" tabindex="0"> <a href="#" id="manage_content_12_ac" tabindex="-1"></div>
-> How to see/activate the content as well?
jQuery...
$("div[role='tabpanel']").show();
or...
$('.accordionItemContent').show();
Run this in console
document.querySelectorAll('div.accordionItemContent.accordion.ui-reset.widget.uibottom[role="tabpanel"]').forEach(e => {
e.style.display = "block";
});
Edited for edited question, only using class accordionItemContent
document.querySelectorAll('div.accordionItemContent[role="tabpanel"]').forEach(e => {
e.style.display = "block";
});
To change the other attributes as mentioned in the update:
document.querySelectorAll('div.accordionItemContent[role="tabpanel"]').forEach(e => {
e.style.display = "block";
e.setAttribute(“tabindex”, 0) // Use this syntax to change all effected attributes
});
One approach could be creating a generic CSS class to hide elements and add/remove it to the element(s) you want to hide/show:
function showDivs() {
document.querySelectorAll('[role=tabpanel]').forEach(elem => elem.classList.remove('hide'));
}
.hide {
display: none;
}
<button onclick="showDivs()">show hidden DIVs</button>
<div class="accordionItemContent accordion ui-reset widget uibottom hide" role="tabpanel">DIV1: Lorem ipsum dolor sit consecutor amet</div>
<div class="accordionItemContent accordion ui-reset widget uibottom hide" role="tabpanel">DIV2: Lorem ipsum dolor sit consecutor amet</div>
<div class="accordionItemContent accordion ui-reset widget uibottom hide" role="tabpanel">DIV3: Lorem ipsum dolor sit consecutor amet</div>
If you prefer to stick with inline CSS, instead, you can modify the code above in this way:
function showDivs() {
document.querySelectorAll('[role=tabpanel]').forEach(elem => {
elem.style.display = 'block';
/*
* NOTE: if the inline style contains only the display property,
* you could even entirely remove it:
*
* elem.removeAttribute('style');
*/
});
}
<button onclick="showDivs()">show hidden DIVs</button>
<div class="accordionItemContent accordion ui-reset widget uibottom" role="tabpanel" style="display: none;">DIV1: Lorem ipsum dolor sit consecutor amet</div>
<div class="accordionItemContent accordion ui-reset widget uibottom" role="tabpanel" style="display: none;">DIV2: Lorem ipsum dolor sit consecutor amet</div>
<div class="accordionItemContent accordion ui-reset widget uibottom" role="tabpanel" style="display: none;">DIV3: Lorem ipsum dolor sit consecutor amet</div>

how to remove specific tags and keep its content

I have some unwanted tags inside contenteditable divs and need to remove them.
Those are and span
Tried with jquery unwrap function but but the result is undefined because it removes parent's tags and not the tags itself.
Any help? In the example below the expected console is:
lorem ipsum lorem ipsum lorem ipsum
$('button').on('click', function(){
$('span').unwrap();
console.log($('#story').html());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='story' id='story'>
loem ipsum <span style="font-size: 1.3em;">lorem ipsum</span> lorem ipsum
</div>
<br>
<button>CLICK</button>
you have to use like
$("#story").find("span").contents().unwrap();
$('button').on('click', function(){
$("#story").find("span").contents().unwrap();
console.log($('#story').html().replace(/ /g, ''));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='story' id='story'>
loem ipsum <span style="font-size: 1.3em;">lorem ipsum</span> lorem ipsum
</div>
<br>
<button>CLICK</button>
You can change outerHTML to innerHTML for <span> and use replace to remove
$('button').on('click', function(){
$('span').each(function(){
this.outerHTML = this.innerHTML;
})
let html = $('#story').html();
$('#story').html(html.replace(/ /g,""))
console.log($('#story').html());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='story' id='story'>
loem ipsum <span style="font-size: 1.3em;">lorem ipsum</span> lorem ipsum
</div>
<br>
<button>CLICK</button>
You need to unwrap the text nodes, so use contents() method to get all child nodes. And replace using String#replace method where html() method with a callback(the second argument in the callback is old HTML content) can be used for updating the content.
$('button').on('click', function() {
// get span tags child nodes and unwrap
$('#story span').contents().unwrap();
// remove from html content
$('#story').html((_, html) => html.replace(/ /g, ''))
console.log($('#story').html());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class='story' id='story'>
loem ipsum <span style="font-size: 1.3em;">lorem ipsum</span> lorem ipsum
</div>
<br>
<button>CLICK</button>

if div is visible do nothing, else .show

I have this HTML code :
<ul>
<li>
<h1>
<span id="span0">Lorem</span>
<p id="p0">Lorem ipsum dolor sit amet,</p>
</h1>
</li>
<li>
<h1>
<span id="span1">Lorem2</span>
<p id="p1">Lorem ipsum dolor sit amet,</p>
</h1>
</li>
<li>
<h1>
<span id="span2">Lorem3</span>
<p id="p2">Lorem ipsum dolor sit amet,</p>
</h1>
</li>
...
</ul>
I have this JS function that shows a paragraph if its sibling span is clicked :
$('span[id^="span"]').click(function() {
$('p[id^="p"]').hide( 900);
$(this).next('p[id^="p"]').show(900);
});
The problem with it is that it will trigger the animation even if the targeted paragraph is already shown(visible).
How would I write an " if " statement that would show targeted paragraph only if its current state is hidden, and do nothing if its already shown?
change this
$(this).next('p[id^="p"]').show(900);
to
$(this).next('p[id^="p"]:hidden').show(900);
use hidden selector to select only those items which are hidden
Just update your click function to following
$('span[id^="span"]').click(function() {
//check if already visible
if(!$(this).next('p[id^="p"]').is(":visible")){
$('p[id^="p"]').hide( 900);
$(this).next('p[id^="p"]').show(900);
}
});

Return DIV to original height after click

I have a 3 DIVs ('event') each with a child DIV ('event-details'). I want to be able to save the height original of 'event-details' (it varies depending on the 'event'), and then set height of 'event-details' to 126px. Then after clicking on a button ('more') I want 'event-details' to return to the original height.
What I have so far saves the 'event-details' heigh, changes it to 126px but after clicking on 'more' it changes the height of 'event-details' to 222px, regardless of the original height it has.
Any help?
JS
$(function() {
$('.event').each(function() {
var eventHeight = $(this).find('.event-details').height();
console.log( eventHeight );
$('.more').on('click', function (e) {
e.preventDefault();
$(this).parent('.event').toggleClass('show');
$('.show > .event-details').css( 'height', eventHeight);
});
});
$('.event-details').css( 'height', '126' );
});
HTML
<div class="event event-1925">
<div class="event-details">
<div class="year">1925</div>
<div class="title">Home Away</div>
<div class="copy">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</div>
</div>
MORE
</div>
<div class="event event-1925">
<div class="event-details">
<div class="year">1925</div>
<div class="title">Home Away</div>
<div class="copy">Lorem Ipsum has been the industry's standard dummy text ever since the 1500s</div>
</div>
MORE
</div>
<div class="event event-1925">
<div class="event-details">
<div class="year">1925</div>
<div class="title">Home Away</div>
<div class="copy">It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</div>
</div>
MORE
</div>
Thanks.
The problem you have is that the variable event height will be reset in the first each loop with the height of the next event div, so essentially you will be left with the height of the final div. You need to store the height of each div in an array or object and then retrieve it.
HTML
Use a unique identifier in the html e.g. an id
<div class="event event-1925">
<div id="event-1" class="event-details">
.....
</div>
</div>
Javascript
$(function() {
//create array to store all the event div heights
var eventHeights = [];
$('.event').each(function() {
var eventHeight = $(this).find('.event-details').height();
console.log( eventHeight );
//get the id of the event div
var id = $(this).find('.event-details').attr('id');
//store the height with reference to the div id
eventHeights[id] = eventHeight;
$('.more').on('click', function (e) {
e.preventDefault();
$(this).parent('.event').toggleClass('show');
//get the id of the event div
var id = $(this).parent('.event').find('.event-details').attr('id');
//use id to get the event div's height from array
$('.show > .event-details').css( 'height', eventHeights[id]);
});
});
$('.event-details').css( 'height', '126' );
});
I have not tested this and it could do with being optimised but hopefully it gives you the idea.
Looks like you are trying to build the expandable field set view. Basically in less View you want to show the title or summary and on expand you show more details. The above answer will work fine. Below is another way you can do it with small animations. To see a working example take a look at this link
your HTML markup for 2 events will be
<div class=" event event-1925">
Event Summary <a class="toogleButton">LESS</a>
</div>
<div class="event-details">
<div class="year">1925</div>
<div class="title">Home Away</div>
<div class="copy">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</div>
</div>
<div class=" event event-1925">
Event Summary <a class="toogleButton">LESS</a>
</div>
<div class="event-details">
<div class="year">1925</div>
<div class="title">Home Away</div>
<div class="copy">Lorem Ipsum is simply dummy text of the printing and typesetting industry.</div>
</div>
The script
$(".event").click(function () {
$header = $(this);
$content = $header.next();
$toogleBut = $header.children().first()
$content.slideToggle(500, function () {
$toogleBut.text(function () {
return $content.is(":visible") ? "LESS" : "MORE";
});
});
});
Finally a simple CSS to set initial styles like your required height
.event{
height:126px;
}

Categories