I have multiple read more read less sections for a page, if I use one read more read less section in page that is working fine if I use multiple sections in a page then there is only one section is working other not working, I need multiple section work in same page, thank in advance.
<script>
const content = document.querySelector(".content-inner");
const contentFull = document.querySelector(".content-full");
const more = document.querySelector(".read-more");
let open = false;
if (more) {
more.addEventListener("click", (e) => {
if (open) {
content.removeAttribute("style");
e.target.innerText = "click here";
open = false;
} else {
content.style.maxHeight = `${contentFull.clientHeight}px`;
e.target.innerText = "read less";
open = true;
}
});
}
</script>
.read-more {
display: inline-block;
margin-top: 10px;
font-weight: 400;
cursor: pointer;
font-size: 14px;
<div class="content">
<div class="content-inner update-modal">
<div class="content-full">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
<span class="read-more">click here</span>
</div>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
This is a task to do with event delegation. Click events (like the most of the events) are bubbling up to document
from the clicked element, and you can detect events on their way up.
The example below shows you how to utilize event delegation. The code is reusable, it can be used on any page with almost any element structure, you can also keep your own class names for the elements, they are passed to the function. This snippet can also be used on dynamic pages, it works without need to make any changes to JS if you add or remove expandable elements to/from the containers.
function toggleContent(options) {
const {container, expandable, expandee, triggerer, autoClose} = options,
contents = document.querySelectorAll(container),
buttonText = ['Read more', 'Read less'];
let current = null; // Keeps book of the currently open expandee
function toggle(e) {
const button = e.target;
if (!button.matches(triggerer)) {return;} // Quit, an irrelevant element clicked
const togglee = button.closest(expandable).querySelector(expandee);
if (!togglee) {return;} // Quit, the element to expand was not found
if (autoClose && current && button !== current) {
// If autoClose is on, closes the current expandee
toggle({target: current});
}
const state = togglee.classList.toggle('visible');
button.textContent = buttonText[state];
current = state ? button : null;
}
// Add click listeners to all elements containing expandables
contents.forEach(cont => cont.addEventListener('click', toggle));
}
// Activate ContentToggler
toggleContent({
container: '.expandables-container', // Selector for the elements containing expandable elements
expandable: '.expandable', // Selector for expandable elements
expandee: '.expandee', // Selector for the elements to expand
triggerer: '.toggle-button', // Selector for the element triggering expansion
autoClose: true // Indicates whether the expanded element is closed when a new element is expanded (optional)
});
.expandable {
border: 1px solid #000;
margin-bottom: 1em;
}
.expandable .expandee {
display: none;
}
.expandee.visible {
display: block;
}
<div class="expandables-container">
<div class="expandable">
<p>Always visible content.</p>
<div class="expandee">
<p>More content.</p>
</div>
<button class="toggle-button">Read more</button>
</div>
<div class="expandable">
<p>Toggle button can be placed above the expandee element.</p>
<button class="toggle-button">Read more</button>
<div class="expandee">
<p>More content.</p>
</div>
</div>
<p>Elements can be placed between expandables.</p>
<div class="expandable">
<p>Always visible content.</p>
<div class="expandee">
<p>More content.</p>
</div>
<p>Elements can be placed between expandable and toggle button.</p>
<button class="toggle-button">Read more</button>
</div>
<div class="expandable">
<p>The only requirement is, that the toggle button and expandee are descendants of expandable.</p>
<div class="expandee">
<p>And the button must not be placed inside expandee.</p>
</div>
<button class="toggle-button">Read more</button>
</div>
</div>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
The idea is, that all the expandables are wrapped in an element. The click listener is attached to that wrapper, and only a single click listener per wrapper is needed. You can also add multiple wrappers to the page.
The "widget" is activated by calling toggleContent
function. Selectors for the wrappers, the expandable elements, the expandee elements and the toggle buttons are passed in an object. The fifth parameter controls the automatic closing, if you want to keep only a single expanded element visible at the time, pass true
, when passing false
, toggle buttons will work independently from each other.
You can test the code at jsFiddle. To see how flexible the snippet is, you can check also this jsFiddle, it uses the same "widget", but with very different markup (a minor change to the widget is, that you can define the texts of the buttons in the options object too).
If you need more control over the toggleable content, I'd recommend OOP based approach. This OOP example wraps the code in the example in this answer in a class, and provides some usecases of how to control the content. A small change is, that you don't have to pass the selector for the expandee, it's defined directly in the stylesheet. When you'll get wind of how OOP works (or you already know), you can easily add methods to the class, ex. for hiding/showing all the content etc.
CodePudding user response:
You need to select the relative selection to perform this operation as the class are repetitive. Here the toggleReadMore function will attach to every read more class and perform the height manipulation action on the content inner class.
var prevOpened = null;
function toggleReadMore(e) {
if(prevOpened !== null && e.target !== prevOpened){
prevOpened.click();
}
// get the current section parent. This will make the selection on other class items easier.
var contentSection = e.target.closest('.content');
var contentInner = contentSection.querySelector('.content-inner');
if (e.target.textContent === "read less") {
contentInner.removeAttribute("style");
e.target.innerText = "click here";
prevClicked = null;
return;
}
contentInner.style.maxHeight = `${contentSection.querySelector('.content-full').clientHeight}px`;
e.target.innerText = "read less";
prevOpened = e.target;
}
var readMoreSections = document.querySelectorAll(".read-more");
readMoreSections.forEach(function(sections) {
sections.addEventListener("click", toggleReadMore);
})
.read-more {
display: inline-block;
margin-top: 10px;
font-weight: 400;
cursor: pointer;
font-size: 14px;
}
.content-inner {
max-height: 20px;
overflow: hidden;
}
<div class="content">
<div class="content-inner update-modal">
<div class="content-full">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
<span class="read-more">click here</span>
</div>
<div class="content">
<div class="content-inner update-modal">
<div class="content-full">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>
</div>
<span class="read-more">click here</span>
</div>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Edit: This has been updated to open only latest clicked item.