How to make a "read more" and a "read less" button in a text that comes dynamically from the database with Pure Javascript?

Asked

Viewed 951 times

3

I would like to make a "read more" and "read less" button with Pure Javascript (no jQuery). However, this text comes dynamically from the database and I use a PHP foreach to fill the Divs with these texts coming from the database.

These texts can have up to 3000 characters, however, I would like to only show 1000, and if the user clicked on "read more" then the 3000 characters would be shown. So if soon after I clicked on "read less", then the text of the div would return to 1000 characters.

I could see some examples, but the text was not dynamically filled and a "span" tag was used embracing the text that would be removed or shown.

Only in my case it is not possible to put this tag "span" precisely because the text is dynamic and comes from the database.

Follows my code:

<?php foreach ($feed['posts'] as $data) { ?>         
      <div class="feed-item-body mt-10 m-width-20 post-body">
           <?php echo nl2br($data->body['body']); ?>
      </div>        
<?php } ?>

My content (text) is in that $data->post['body'].

Below is my code in a more verifiable way. Let’s imagine that the text inside the div came from the bank

<div class="feed-item-body mt-10 m-width-20 post-body">

    (IMAGINEM QUE ESSE SEJA UM TEXTO COM 3000 CARACTERES) 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, when an unknown printer took a galley of type and scrambled it to make a type specimen book.

</div>        

There’s this example here from w3Schools: Example:

But note that it is a static text and they use a span. If in my case it was a static text, it would be much easier.

So how would a Javascript code look to show and collect the text according to its number of characters ?

  • Related: https://answall.com/a/169912/4995

  • Related: https://answall.com/questions/117961

  • 1

    I’ll take a look. Thank you. I did a lot of research and was finding nothing that came close to my toil. I’ll see.

  • 1

    You asked the other question if the "text dies". Yes, it dies. The idea is to have two div. A with the full text you leave hidden, and a with cut text you leave displaying. When you click on the link, you display the full text and hide the other.

  • 1

    "I would like to make a "read more" and "read less" button with Pure Javascript (no jQuery). However, this text comes dynamically from the database and I use a PHP foreach to fill the Divs with these texts coming from the database" I can’t see where this changes anything, after PHP has run the result is a static HTML. JS only runs after.

  • Even, I included the [php] tag, since most solutions can be found in php itself, which is used in the question.

  • @Bacco, then. I had the view that changes, because when they are static texts, we can fit tags as span within these texts. So we can hide these texts that are inside these tags easily, and then show them through a JS event. However, with the dynamic text coming from the bank via PHP, this text is already mounted and there would be no way to put tags in the middle of it to hide part of the text. I don’t know if I can express myself well...

  • @Wallacemaxters, but this idea of yours was a great idea. I will try to apply it here and return with the result. I think you could even work out a response based on what you said. You don’t have to code. Explaining in context I already accept it. Thank you!

  • @Gatodeschrödinger understand I understood, I just don’t know how to make it fit on the site, the question doesn’t have a [mcve], other alternatives have already been posted with CSS on the site, the way it is was a "do it for me", even if it’s not the intention. A [mcve] would be fundamental to keep the post open (having as someone copy your PHP with a real text (same as the database) mounting a div.

  • With CSS I believe it already has solution on the site, including (limiting space with CSS div and overflow:Hidden) - even for modern browsers or need JS https://answall.com/a/110958/70

  • @Bacco , I will delete the post. Good

  • 1

    Did you see the CSS example? There are many ways to adapt, you can change the class with JS if you want to avoid checkbox. Better than erasing would be [Edit] and for the minimum verifiable example, then it would be useful for a broad audience

  • My intention was not a do for me. It’s because I didn’t even know. And I really wanted in JS Vanilla. That’s why I made it clear in the title of the question.

  • So. The Minimal and Verifiable Example helps everyone. Until you get on top of it we can come up with an official answer that solves what you need and serves those who search it later. That’s the idea of the site. And it’s something you do in a minimal amount of time,

  • @Bacco , but the minimum and verifiable example is just the one I have. Only the string is displayed via PHP and as it comes from the database. Other than that, I wouldn’t have any more useful code.

  • 2

    I think it’s worth a read on [mcve]. Verifiable, minimal and complete is if I take the code, save it as PHP here (or open it in an ideone) and exit the HTML the way you have today. That is for us to have the same result as you, but without creating a bank for it (ex, with a variable in place). And "minimum" means taking away everything that doesn’t change the problem. If you master the concept, you make your time on the site more income for you and everyone involved.

Show 11 more comments

4 answers

4


You can do with the following steps:

  • Check if the text passes the desired size with a if in PHP.
  • Limit text to the desired size within a div if the size passes the PHP limit.
  • Create a div with the full content, but hidden.
  • Create a link to expand next to the cut text and a link to reduce the full text.
  • Apply Javascript.

For example, your PHP code could look like this:

<?php foreach ($feed['posts'] as $data) { ?>      
      <div class="feed-item-body mt-10 m-width-20 post-body">
           <?php if (mb_strlen($data->post['body'], 'UTF-8') > 1000): ?>
               <span class="short-text">
                   <?php echo nl2br(mb_substr($data->post['body'], 0, 1000, 'UTF-8')); ?> 
                   ... 
                  <a class="read-more">leia mais</a>
               </span>
               <span class="full-text" style="display: none"><?php echo nl2br($data->post['body']) ?> <a class="read-less">ler menos</a></span>

           <?php else: ?>
               <span class="full-text"><?php echo nl2br($data->post['body']) ?></span>
           <?php endif ?>
      </div>        
<?php } ?>

With Javascript, you could do this:

document.querySelectorAll('.post-body').forEach(function (el) {
   
   var fullText = el.querySelector('.full-text');
   var shortText =  el.querySelector('.short-text');
   
   if (! shortText) return;
   
   el.querySelector('.read-more').addEventListener('click', function () {
      fullText.style.display = '';
      shortText.style.display = 'none';
   })
   
   el.querySelector('.read-less').addEventListener('click', function () {
      fullText.style.display = 'none';
      shortText.style.display = '';
   })
})
a {
   color: blue;
   cursor: pointer;
}
<div class="feed-item-body mt-10 m-width-20 post-body">
     <span class="short-text">Meu texto está ... <a class="read-more">ler mais</a></span>
     <span class="full-text" style="display: none">Meu texto está aqui firme e forte <a class="read-less">ler menos</a></span>
</div>

<div class="feed-item-body mt-10 m-width-20 post-body">
     <span class="short-text">Meu texto está ... <a class="read-more">ler mais</a> </span>
     <span class="full-text" style="display: none">Meu texto está aqui firme e forte <a class="read-less">ler menos</a></span>
</div>

<div class="feed-item-body mt-10 m-width-20 post-body">
     <span class="full-text">Meu texto pequeno</span>
</div>

Note that on the last div, I simulated the case of falling in the else PHP and the text is displayed normally because it does not exceed 1000.

I won’t explain about mb_string instead of substr, because that response from Bacco already explains everything.

1

Well I found a lot of similar things as pointed out in the comments, look you can do something like the Js below (more can do with the same php syntax or javascript same:

function readMoreRome() { //finds function
var dots = document.getElementById("dots"); //returns element that has the ID attribute with value, searches for dots
var moreText = document.getElementById("more"); // '' '' searches for more
var btnText = document.getElementById("myBtn"); // '' '' searches for myBtn

if (dots.style.display === "none") {
    dots.style.display = "inline";
    btnText.innerHTML = "Read more"; //button says read more to show more text
    moreText.style.display = "none";
} else {
    dots.style.display = "none";
    btnText.innerHTML = "Read less"; //button says read less to show less text
    moreText.style.display = "inline";
}
}

function readMoreBuda() { //finds function
var dots = document.getElementById("dots2"); //returns element that has the ID attribute with value
var moreText = document.getElementById("more2"); // '' '' searches for more2
var btnText = document.getElementById("myBtn2"); // '' '' searches for myBtn2

if (dots.style.display === "none") {
    dots.style.display = "inline";
    btnText.innerHTML = "Read more"; //button says read more to show more text
    moreText.style.display = "none";
} else {
    dots.style.display = "none";
    btnText.innerHTML = "Read less"; //button says read less to show less text
    moreText.style.display = "inline";
}
}

HTML:

<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. It is a relatively small city, however there are much to see and do.
    <span id="dots2">...</span>
    <span id="more2">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 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, framed for its individuality, cultural interest, and physical beauty, home to art and architecture. Facing the Mediterranean to the southeast,
    <span id="dots3">...</span>
    <span id="more3"> 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 onclick="readMoreBarca()" id="myBtn3">Read more</button>
</div>

No matter if your data comes from a database or not, to display and display "less" the logic is always the same, takes the idea in the example :)

You have more English even more than one answer and explanation if you want to understand better reference: https://stackoverflow.com/questions/59360119/how-to-make-multiple-read-more-read-less-buttons-in-the-same-one-page

  • I appreciate the attempt, Herick Eduardo, but the example you cited is static texts. It is more or less like the example of w3Schools that I quoted there in my question. This example of yours would not suit me.

  • Okay, I’m sorry, I’m going to leave the comic in case someone from here has the same problem maybe the example fits another context etc.

  • Nothing. The attempt to help is always valid.

0

Following the answer and tips in the comments of Wallace Maxters, I was able to adapt my code and make it functional, based only on JS (with a minimum of PHP).

I’m leaving my code only to help someone who, in the future, is going through something similar to what I went through.

My code went like this:

<?php foreach ($feed['posts'] as $data) { ?>   
  <div class="box-body">      
       <div class="feed-item-body mt-10 m-width-20 post-body">
            <?php echo nl2br(mb_strimwidth($data->post['body'], 0, 1000, ' ...')); ?>
            <button class="ler-mais">Ler mais</button> 
       </div>  
      
       <div class="feed-item-body mt-10 m-width-20 post-body-2">
            <?php echo nl2br($data->post['body']); ?>
            <button class="ler-menos">Ler menos</button> 
       </div> 
  </div> 
<?php } ?>

The JS was like this:

//Código do botão leia mais
document.querySelectorAll('.ler-mais').forEach(function (el) {
   
   el.addEventListener('click', function () {
      let divBoxBody = el.closest('.box-body'); 
      let divPostBody = divBoxBody.querySelector('.post-body').style.display = "none";   
      let divPostBody2 = divBoxBody.querySelector('.post-body-2').style.display = "block"; 
   })   
   
})

//Código do botão leia menos
document.querySelectorAll('.ler-menos').forEach(function (el) {
   
   el.addEventListener('click', function () {
      let divBoxBody = el.closest('.box-body'); 
      let divPostBody = divBoxBody.querySelector('.post-body').style.display = "block";   
      let divPostBody2 = divBoxBody.querySelector('.post-body-2').style.display = "none"; 
   })   
   
})

I have two divs. A I leave apparent (with partial text, displaying only 1000 characters [for this we use PHP mb_strimwidth() ] of the data coming from the database). And the other I leave hidden (with display:None) with the whole text, displaying all the text that comes from the database.

When I click on the "read more" button, the div with the partial content becomes hidden (with display:None) and the div with the entire content becomes visible (with display:block).

When I click on the "read less" button the reverse of what was done above is done.

Thank you to those who helped and hope to help too.

-1

Here’s the example, you can use map((data, id)) function to manipulate the data within the array and substring to determine the text size, I created 2 examples to help you, in the second example I created the getData function as if I had taken the API data.

const data = [
  {
    titulo: 'hello',
    text: 'hello world com 3000 caracteres',
  },
];

const leiaMais = () => {
  document.getElementById('leia').innerHTML = 'Leia Menos';
  document.getElementById('leia').onclick = leiaMenos;
  data.map(({ text }, id) => {
    document.getElementById('textoApi').innerHTML = `<p>${text.substring(0, text.length)}</p>`;
  });
};

const leiaMenos = () => {
  document.getElementById('leia').innerHTML = 'Leia Mais';
  document.getElementById('leia').onclick = leiaMais;
  data.map(({ text }, id) => {
    document.getElementById('textoApi').innerHTML = `<p>${text.substring(0, 11)}</p>`;
  });
};

// exemplo com async

async function getData() {
  return data;
}

const leiaMaisAsync = () => {
  document.getElementById('leiaAsync').innerHTML = 'Leia Menos Async';
  document.getElementById('leiaAsync').onclick = leiaMenosAsync;
  getData().then((data) => {
    data.map(({ text }, id) => {
      document.getElementById('textoApiAsync').innerHTML = `<p>${text.substring(0, text.length)}</p>`;
    });
  }).catch(() => {
    // algo errado aconteceu
  });
};

const leiaMenosAsync = () => {
  document.getElementById('leiaAsync').innerHTML = 'Leia Mais Async';
  document.getElementById('leiaAsync').onclick = leiaMaisAsync;
  getData().then((data) => {
    data.map(({ text }, id) => {
      document.getElementById('textoApiAsync').innerHTML = `<p>${text.substring(0, 11)}</p>`;
    });
  }).catch(() => {
    // algo errado aconteceu
  });
};
<!DOCTYPE html>
<html lang="pt-br">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link
      rel="stylesheet"
      href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
      integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
      crossorigin="anonymous"
    />
    <script
      src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
      integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
      crossorigin="anonymous"
      defer
    ></script>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
      integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
      crossorigin="anonymous"
      defer
    ></script>
    <script
      src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
      integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
      crossorigin="anonymous"
      defer
    ></script>
    <script src="scripts/main.js" defer></script>
    <title>Javascript Teste</title>
  </head>
  <body>
    <div class="container">
      <div>
          <button
            class="btn btn-link collapsed"
            onclick="leiaMais()"
            id="leia"
            data-toggle="collapse"
            data-target="#collapseTwo"
            aria-expanded="false"
            aria-controls="collapseTwo"
          >
            Leia Menos
          </button>
          <div class="card-body" id="textoApi"></div>
      </div>
      <div>
        <button
          class="btn btn-link collapsed"
          onclick="leiaMaisAsync()"
          id="leiaAsync"
          data-toggle="collapse"
          data-target="#collapseTwo"
          aria-expanded="false"
          aria-controls="collapseTwo"
        >
          Leia Menos Async
        </button>
        <div class="card-body" id="textoApiAsync"></div>
      </div>
    </div>
  </body>
</html>

Browser other questions tagged

You are not signed in. Login or sign up in order to post.