0

I am using ACF repeater to create user profiles that have a "read more/read less" function that shows and hides text. The issue I'm facing is when I press the "read more" button for the 3rd profile down, it opens the first profile. How do I make this functionality dynamic so it only opens/closes the profile being clicked?

<div class="container" style="margin-top: 60px;">
        <?php if(have_rows('profile')): ?>
        <?php while(have_rows('profile')): the_row(); ?>
            <div class="row">
                <div class="col-md-3">
                    <div class="people-img" style="background-image: url('<?php echo the_sub_field("profile_img"); ?>')"></div>
                        </div>
                        <div class="col-md-9">
                        <h4 class="profile-name">
                        <?php the_sub_field('name'); ?>
                        </h4>
                    <p class="profile-job-title semi-bold">
                                <?php the_sub_field('info'); ?>
                    </p>

                    <p><?php the_sub_field('profile_blurb'); ?><span id="dots">...</span><span id="more"><?php the_sub_field('profile_read_all'); ?></span></p>
                    <div class="about-readmore" onclick="myFunction()" id="myBtn">Read more</div>
                  <div class="readmore-border"></div>
                </div>
            </div><!-- end row --> 
    <div class="profile-border"></div>
 <?php
endwhile;
else:
endif;
?>

<script>
function myFunction() {
  var dots = document.getElementById("dots");
  var moreText = document.getElementById("more");
  var btnText = document.getElementById("myBtn");

  if (dots.style.display === "none") {
    dots.style.display = "inline";
    btnText.innerHTML = "Read more"; 
    moreText.style.display = "none";
  } else {
    dots.style.display = "none";
    btnText.innerHTML = "Read less"; 
    moreText.style.display = "inline";
  }
}
</script>

#more {display: none;} 
user2684452
  • 701
  • 2
  • 14
  • 31
  • 1
    HTML IDs must be unique. `document.getElementById("more")` should always return the first `#more` element that is available in the DOM. – Wild Beard Dec 14 '18 at 16:27
  • Possible duplicate of [getElementById returning value only for first element](https://stackoverflow.com/questions/24123707/getelementbyid-returning-value-only-for-first-element) – Heretic Monkey Dec 14 '18 at 16:37

2 Answers2

1

In this code:

var dots = document.getElementById("dots");
var moreText = document.getElementById("more");
var btnText = document.getElementById("myBtn");

You are selecting the first occurrence of the element with id = "dots". That's always the 1st profile in your list.

1

One simple way to solve this would be:

  1. change the <div id="dots"> to <div class="dots">
  2. parameterize this function with a count, then look up the nth occurrence of
  3. use document.querySelectorAll to look up all ".dots" elements, then expand the one using the passed in index to myFunction(index). You can use a php counter var to track index.

    <div class="about-readmore" onclick="myFunction(index)" id="myBtn">Read more</div>

2

Another, perhaps more fragile way to do it would be to look up and down the DOM to find the nearest profile to expand:

function myFunction(event) {
  var targetElementParent = event.target.parent;
  var moreText = targetElementParent.querySelector(".more");

  // The rest of the function could remain mostly the same

I say this is fragile because it relies on the structure of the DOM pretty heavily so if you change that, the function will break. In this case, as in #1, you should change the ids "dots", "more" and "myBtn" to classes because those elements are repeated.

From MDN Id attribute page:

The id global attribute defines a unique identifier (ID) which must be unique in the whole document

tmeans
  • 175
  • 1
  • 6
-1

Ordinarily, you would expect to see that each block of text – each "profile," as you call them – would have a unique id in the HTML/DOM. Let's say for the sake of example that the id of one of the blocks is "123." The HTML for that profile would then be generated to contain onclick="myFunction(123)".

Now, each onClick is different – it supplies the id that the function is supposed to act upon, and this function should always see that getElementById returns a valid object because an element with that id should always be present. (If it doesn't, there's a bug in the generated HTML, and it should complain.)

Mike Robinson
  • 8,490
  • 5
  • 28
  • 41