0

I have a button that should hidden area with nice animation. It's using height attribute to make the animation.

The issue is, when I add content to the "hiddenable" area, the content isn't viewable as it's outside the given height. And I have to clic again on "Open" to re-hide and re-open the area.

Here is a MRE :

function changeVisility(all, toCancel = null) {
    if(toCancel != null)
        toCancel.preventDefault();
    if(!(Symbol.iterator in Object(all)))
        all = [ all ];
    for(var div of all) {
        for(const wrapper of div.querySelectorAll(".collapse-wrapper")) {
            if(wrapper.style.height == '0px') {
                wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
            } else {
                wrapper.style.height = '0px';
            }
        }
    }
}

for(const wrapper of document.querySelectorAll(".collapse-wrapper")) {
    if(wrapper.style.height != '0px') { // set real height
        wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
    } else
        wrapper.style.height = '0px';
}

function addContent() {
    document.getElementById("contentToAdd").insertAdjacentHTML("beforeend", "<p>Other text</p>");
}
.collapse-wrapper {
    overflow: hidden;
    transition: height 300ms ease-in;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">

<body style="margin-left: 10px;">
<div class="form-check" style="padding-top: 0.5rem;">
    <input class="form-check-input" type="checkbox" name="newinput" id="newinput" onchange="changeVisility(document.getElementById('input_selection'));">
    <label class="form-check-label" for="newinput">
        Open
    </label>
</div>
<div id="input_selection">
    <div style="height: 0px;" class="collapse-wrapper">
        <div class="collapse-content row" id="contentToAdd" style="background-color: red;">
            <button onclick="addContent()" type="button" class="btn btn-primary">Add</button>
            <p>Some text</p>
        </div>
    </div>
</div>
</body>

To reproduce :

  1. Clic on "Open" to open the area
  2. Clic on "Add" - Nothing show (but the text is well added to HTML)
  3. Clic on "Open" twice (to hide then re-open)
  4. See the red area upgrade with "Other text" showed

How can I make them be showable and keeping the nice animation ?

I tried to set height to null to show, but there is no longer animation...

7
  • Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. What should be happening, it is not clear from your description? Commented Jul 19, 2024 at 13:23
  • @Paulie_D I explained 2 times in the post the issue: when clicking on "Add" it add div to HTML, a div that is not visible but should be... Commented Jul 19, 2024 at 13:29
  • I'm guessing it's something like this - stackoverflow.com/questions/3747683/… You want the new div to just slide down to it's natural height? Commented Jul 19, 2024 at 13:29
  • After you added content, you just need to do the exact same thing you did before - measure the actual height of the .collapse-content, and assign that as the new height for the .collapse-wrapper. Commented Jul 19, 2024 at 13:30
  • @CBroe I was mostly looking for an "automatic" way, to don't have to recalculate myself. Also, the animation is important for the hide/show of the full .collapse-content, not specially for the added content Commented Jul 19, 2024 at 13:42

1 Answer 1

1

Everytime content is added you need to recalculate the new height. addContent(e) has been changed:

function addContent(e) {
  this.parentElement.insertAdjacentHTML("beforeend", "<p>Other text</p>");
  const wrapper = this.closest(".collapse-wrapper");
  wrapper.style.height = wrapper.querySelector(".collapse-content")
    .getBoundingClientRect().height + `px`;
}

In fact I changed everything so it's no longer dependent on #ids thus allowing you to add as many dropdowns as you like. Every <input> needs class="toggle" and every <button> needs class="add" (See Example 1). BTW the animated dropdown can be done with CSS and no JavaScript at all (see Example 2 and this article).

Example 1

function changeVisility(e) {
  const wrapper = this.closest(".form-check").nextElementSibling.querySelector(".collapse-wrapper");
  if (wrapper.style.height == '0px') {
    wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
  } else {
    wrapper.style.height = '0px';
  }
}

function addContent(e) {
  this.parentElement.insertAdjacentHTML("beforeend", "<p>Other text</p>");
  const wrapper = this.closest(".collapse-wrapper");
  wrapper.style.height = wrapper.querySelector(".collapse-content").getBoundingClientRect().height + `px`;
}

document.querySelectorAll(".toggle").forEach(t => t.onchange = changeVisility);

document.querySelectorAll(".add").forEach(a => a.onclick = addContent);
.collapse-wrapper {
  overflow: hidden;
  transition: height 300ms ease-in;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">

<body style="margin-left: 10px;">
  <div class="form-check" style="padding-top: 0.5rem;">
    <label class="form-check-label">
      <input class="toggle form-check-input" type="checkbox" name="newinput">
      Open
    </label>
  </div>
  <div id="input_selection">
    <div style="height: 0px;" class="collapse-wrapper">
      <div class="collapse-content row" style="background-color: red;">
        <button type="button" class="add btn btn-primary">Add</button>
        <p>Some text</p>
      </div>
    </div>
  </div>
</body>

Example 2

function addContent(e) {
  const wrapper = this.closest(".collapse-wrapper");
  wrapper.querySelector(".collapse-content").insertAdjacentHTML("beforeend", `<p>More text</p>`);
}

document.querySelectorAll(".add").forEach(a => a.onclick = addContent);
.toggle {
  display: none;
}

.collapse-wrapper {
  height: auto;
  max-height: 0px;
  overflow: hidden;
  transition: max-height 0.7s ease-in;
}

.toggle:checked~.collapse-wrapper {
  max-height: 100vh;
}

.form-check-label {
  display: block;
  width: 90%;
  padding: 0 0 1ex 1ch;
  text-align: center;
  cursor: pointer;
}

.form-check-label::before {
  content: "Open";
}

.toggle:checked+.form-check .form-check-label::before {
  content: "Close";
}

.btn-block {
  width: 100%
}

p {
  padding: 1ex 2ch;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">

<body>
  <main class="container ml-1">
    <section class="row">
      <input id="t1" class="toggle" type="checkbox">
      <div class="form-check col-12" style="padding-top: 0.5rem;">
        <label for="t1" class="form-check-label"></label>
      </div>
      <div class="collapse-wrapper row">
        <div class="collapse-content bg-light col-12">
          <button class="add btn btn-block btn-primary" type="button">Add</button>
          <p>Some text</p>
        </div>
      </div>
    </section>
  </main>
</body>

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks you ! I will test it monday. I will try without #id, that's a good point but also a way that can like open the wrong collapse wrapper xd I'll see. For your second example, it seems there is 1 or 2 seconds or "lag" while closing.
For smoother animation try CSS @keyframes. As for getting the right behavior from the wrong element that all depends on how your HTML is structured and the specificity of your CSS and/or the control your JavaScript has over the DOM.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.