Clicking on the Read Less button causes it to shift position instead of returning to its original placement

My website features a convenient Read More/Read Less button to manage lengthy text sections. You can check out my website at the link provided: () However, I have encountered an issue when using the Read Less Button to collapse the expanded area—it doesn't return to the original position on the page. It seems to lose its place entirely. Below is an example of the code I have implemented.

function readMoreRome() { //locates function
    var dots = document.getElementById("dots"); //finds element with ID attribute value 'dots'
    var moreText = document.getElementById("more"); // finds element with ID attribute value 'more'
    var btnText = document.getElementById("myBtn"); // finds element with ID attribute value 'myBtn'

    if (dots.style.display === "none") {
        dots.style.display = "inline";
        btnText.innerHTML = "Read more"; //displays 'Read more' when clicked to show more text
        moreText.style.display = "none";
    } else {
        dots.style.display = "none";
        btnText.innerHTML = "Read less"; //displays 'Read less' when clicked to show less text
        moreText.style.display = "inline";
    }
}

function readMoreBuda() { //locates function
    var dots = document.getElementById("dots2"); //finds element with ID attribute value 'dots2'
    var moreText = document.getElementById("more2"); //finds element with ID attribute value 'more2'
    var btnText = document.getElementById("myBtn2"); //finds element with ID attribute value 'myBtn2'

    if (dots.style.display === "none") {
        dots.style.display = "inline";
        btnText.innerHTML = "Read more"; //displays 'Read more' when clicked to show more text
        moreText.style.display = "none";
    } else {
        dots.style.display = "none";
        btnText.innerHTML = "Read less"; //displays 'Read less' when clicked to show less text
        moreText.style.display = "inline";
    }
}
<div class="card">
    <h2>Visit Budapest</h2>
    <div class="info"> <span class="date"><i class="far fa-calendar"></i> November 12, 2019</span> <span class="comment"><i class="far fa-comment-alt"></i> 2 comments</span> </div>
    <div class="img"><img src="img/szechenyi.jpg" style="height:200px;"> </div>
    <p><i>Széchenyi Thermal Baths </i></p>
    <p>
        Budapest is the capital city of Hungary. It is best known for its arts and culture. Despite being relatively small, there are numerous attractions to explore.
        <span id="dots2">...</span>
        <span id="more2">Located on thermal springs, Budapest features many naturally heated baths such as the Széchenyi baths which boast 15 indoor baths and 3 outdoor ones. The city also offers breathtaking viewpoints, ideal for capturing panoramic views. From the 360-degree vistas at St Stephens Basilica to the scenic overlook of the parliament and river at Fisherman’s Bastion—there's something for everyone. Don't miss visiting the Museum of Fine Arts to immerse yourself in famous European art. If you're a fan of classical music, be sure to catch a performance at the Academy of Music.</span>
    </p>
    <button onclick="readMoreBuda()" id="myBtn2">Read more</button>
</div>
<br>
<div class="card">
    <h2>Visit Barcelona</h2>
    <div class="info"> <span class="date"><i class="far fa-calendar"></i> December 06, 2019</span> <span class="comment"><i class="far fa-comment-alt"></i> 5 comments</span> </div>
    <div class="img"><img src="img/guell-park.jpg" style="height:200px;"></div>
    <p><i>Park Güell </i></p>
    <p>
        Barcelona, renowned for its unique culture, artistic appeal, and stunning landscapes, boasts unparalleled art and architecture. Overlooking the Mediterranean Sea in the southeast,
        <span id="dots3">...</span>
        <span id="more3">the city embodies a one-of-a-kind charm. When in Barcelona, make sure to visit the exquisite Park Güell, initially designed by artist Antoni Gaudí for a mountain town. Gaudí's architectural style is globally admired for its uniqueness and distinctiveness. Another must-see attraction is La Sagrada Família, a massive basilica. With beaches nearby, abundant art, and rich urban culture, this diverse city offers a plethora of experiences.</span>
    </p>
    <button onclick="readMoreBarca()" id="myBtn3">Read more</button>
</div>

Answer №1

It's important to follow the DRY principle in your code - Don't Repeat Yourself. By letting the machine do the work for you instead of writing a handler function for every card, you can make it more convenient to update and maintain the page. This approach involves using id-less markup as shown below:

const allCards = document.querySelectorAll('.all-cards');

function toggleText (e) {
    if (e.target.tagName !== 'BUTTON') {return;} // Quit, not clicked on a button
    const button = e.target,
    card = e.target.parentElement,
    hide = card.querySelector('.three-dots'),
    show = card.querySelector('.expanded-text'),
    state = !+button.getAttribute('data-state'),
    pos = +button.getAttribute('data-pos');

  // Make the changes to the page
  hide.classList.toggle('hidden');
  show.classList.toggle('hidden');
  button.setAttribute('data-state', +state);
  button.textContent = (state) ? 'Read less' : 'Read more';
  
  // Save/adjust scroll position
  // posDelta depends on the space to scroll after the last card.
  // If there is space, toggle the comment on the const lines below
  const posDelta = (button.parentElement === allCards.lastElementChild) ? 0 : -show.offsetHeight;
  // const posDelta = -show.offsetHeight;  
  if (state) {
    e.target.setAttribute('data-pos', posDelta);
  } else {
    window.scrollBy(0, pos);
  }

  return;
}

// Attach an event listener for each `.all-cards` element on the page
allCards.forEach(card => card.addEventListener('click', toggleText));
.hidden {
  display: none;
}
.card img {
  height: 200px;
}
<div class="all-cards">
  <div class="card">
      <h2>Visit Budapest</h2>
      <div class="info"> <span class="date"><i class="far fa-calendar"></i> November 12, 2019</span> <span class="comment"><i class="far fa-comment-alt"></i> 2 comments</span> </div>
      <div class="img"><img src="img/szechenyi.jpg"> </div>
      <p><i>Széchenyi Thermal Baths </i></p>
      <p>
          Budapest is the capital city of Hungary. It is best known for its arts and culture. It is a relatively small city, however there are much to see and do.
          <span class="three-dots">...</span>
          <span class="expanded-text hidden">Situated on thermal springs, there are many naturally heated baths to relax in, the Széchenyi baths are the largest with 15 indoor baths and 3 outdoor. There are many spectacular viewpoints in Budapest, great for capturing the views of the city. From 360 panoramic views up at St Stephens Basilica to a wide view of the parliament and the River at Fisherman’s Bastion. Visit the Museum of Fine Arts and enjoy a day amongst famous European art. Classical music lovers will appreciate a performance at the Academy of Music.</span>
      </p>
      <button data-state="0">Read more</button>
  </div>
  <br>
  <div class="card">
      <h2>Visit Barcelona</h2>
      <div class="info"> <span class="date"><i class="far fa-calendar"></i> December 06, 2019</span> <span class="comment"><i class="far fa-comment-alt"></i> 5 comments</span> </div>
      <div class="img"><img src="img/guell-park.jpg"></div>
      <p><i>Park Güell </i></p>
      <p>
          Barcelona, framed for its individuality, cultural interest, and physical beauty, home to art and architecture. Facing the Mediterranean to the southeast,
          <span class="three-dots">...</span>
          <span class="expanded-text hidden"> the city is one of a kind. Upon visiting make sure you visit the spectacular and unique Park Güell which was firstly designed for a town up in the mountains by artist Antoni Gaudí. Gaudí's work is admired by architects around the World as being one of the most unique and distinctive styles in modern architecture. Other places worth visiting is the La Sagrada Família, is a giant basilica. With beaches on your doorstop, and art and city culture, this diverse city has everything to offer.</span>
      </p>
      <button data-state="0">Read more</button>
  </div>
</div>

By eliminating ids and inline events while adding the hidden class to initially hide elements, the code becomes cleaner and more efficient. Instead of inline events, a single click event is delegated to the wrapper element of all cards using the addEventListener method in JavaScript. The handler function then identifies the clicked button within the event object passed automatically by the browser.

The target elements in the HTML are labeled with class attributes (three-dots and expand-text). The code uses data-pos and data-state attributes to manage expanding and collapsing text sections, ensuring smooth user interaction. Inline styles are replaced with class toggles, simplifying the code structure.

Ensure that the button remains a direct child of the card div for the functionality to work correctly. You may adjust other parts of the markup structure as long as the required classes are retained for the expandable elements.

If there will always be content after the cards or the page has a sufficient bottom margin, you can simplify the posDelta calculation as mentioned in the code comments.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Import Socket.io into your Node.js application using the import statement

Can't seem to figure out why I keep encountering this error. Everything works perfectly when I use the request method instead. import express from 'express'; import { createServer } from 'http'; import * as io from 'socket.io& ...

How can I prevent a postback from occurring when using a jQuery dialog with a

$(document).ready(function () { $("#TaskCreatorDivButton").click(function () { $("#taskCreationForm").dialog( { width: "600px" } ); }); }); ...

The variable in the dataTables JavaScript is not receiving the latest updates

//update function $('#dataTable tbody').on('click', '.am-text-secondary', function() { //extract id from selected row var rowData = table.row($(this).parents('tr')).data(); var updateId = rowData.id; ...

Troubleshooting issue with Angular's inability to loop through a multi-dimensional JSON

I recently started experimenting with Angular and decided to create a shopping cart application. I found a pre-made site template that organizes items into categories using the following structure: <div class="row"> <ul> <li>item1</li ...

Passing the date as a JSON response from a web API to AngularJS

Seeking advice on date formatting. I am currently developing an AngularJS application that retrieves data from an ASP.NET Web API. Within a table (Model), there is a date field (ThisDate) with the following format: 2015-03-14 12:39:32.470 When I pass th ...

The width specified for the table is not being applied

I've been struggling to adjust the width of my table in order to display data from a database. I've tried using inline width, !important tag, id, and class but none have worked. Interestingly, when I apply a fixed width without dynamic data, it w ...

The jQuery execCommand feature fails to generate a pop-up when used within the contenteditable attribute of an HTML tag

Trying to implement an inline text editor using the jQuery execCommand function. Below is a snippet of my source code: /*******************************************************************/ /********** Click: inner of contenteditable text-editor div *** ...

Struggling to display the retrieved data on the webpage

Code in pages/index.js import React from 'react'; import axios from 'axios'; import ListProducts from '@/components/products/ListProducts'; const getProducts = async () => { const data = await axios.get(`${process.env.AP ...

Guide to verifying the presence of cookies by name in the browser and granting access to the specific page accordingly

In the process of developing an authorization system with Express, Node, and MySQL, I decided to utilize JWT tokens for user authorization. After successfully storing the JWT token in cookies, my next step is to verify if the token exists in the cookie b ...

Switch between various chart types in Highcharts for multiple series by utilizing a dropdown menu

Hey there, I'm new to the world of programming and I'm currently working on creating a chart with a drop-down list for chart types. I've tried several solutions suggested here, but unfortunately, none of them seem to work with my code. Any h ...

The dynamic component remains unresponsive when prompted

When I try to add an item as a submenu in my menu by clicking a button, the jQuery code for the parent items does not function as expected. $('.menu li.has-sub>a').on('click', function() { alert("Working"); }); $(".test").click ...

linking together javascript methods

After spending over an hour searching on SO, I couldn't find a similar case to mine. I have developed a web app using Ruby on Rails. The app consists of a form with multiple fields and buttons that trigger various actions. Here are some examples (my ...

What is the best way to extract the text from a <div> tag and show it in a different div when clicked on using ng click in AngularJS?

When the button is clicked, the text in ng-model=name should be displayed in the <h2> tag. <div ng-model="name">This is a text</div> <button ng-click="getname()">Click</button> <h2>{{name}}</h2> ...

AngularJS and Karma: Revoking ng-dirty status using setPristine

I'm currently facing an issue in one of my unit tests where I am testing the removal of ng-dirty when using setPristine on an input element. Even after calling setPristine, the ng-dirty is not being removed. My suspicion is that I may be incorrectly ...

Jquery Banner Fade In animation malfunctioning

I am facing an issue with my banner on various browsers, especially IE 7,8, and possibly 9. When the fade-in effect is applied at the bottom of the banner, the shadows underneath turn black. Is there anyone who can help me resolve this issue? Website: ww ...

How can you incorporate Vue components with tabindex?

I have successfully implemented a custom view component that displays three select lists for "Year", "Make", and "Model". Everything is functioning as intended. However, I encountered an issue when dynamically placing this component on a parent form. The ...

pulsate the text by applying a transform css to it

I have an SVG template with some text that I want to make pulsate. Here is the HTML code: <tspan class="text animated pulse" x="14.325" y="39.18">50</tspan></text> Unfortunately, it's not working as expected. If you'd like ...

Initial argument for the event listener

If I have event handlers registered inline in my markup (even though it's deprecated) like span id="..." onclick="foo(p1,p2,p3)" how do I access the "event" object in the event handler function foo? Is changing the above to span ...

Place the button using the "fixed" position in a React Native application

Is there a way to place a button in the bottom right corner with a fixed position in React Native? The position: fixed property does not work in React Native, and the stickyHeaderIndices method in ScrollView does not allow positioning an element above oth ...

Ensure that the input field only accepts numerical values

Can anyone help me with an issue I'm facing in my plunker? I have an input text field that I want to accept only numbers. Despite trying normal AngularJS form validation, the event is not firing up. Has anyone encountered a similar problem or can prov ...