Issue with extraneous event being activated upon bootstrap collapse button usage

I have implemented a functionality using Bootstrap 5.2 where I have two buttons that can hide content by utilizing the Bootstrap collapse plugin.

       <div class="col-12 col-sm-auto">
            <span class="pe-2">
                <button id="show_movements_button" type="btn" class="btn btn-outline-primary" data-bs-toggle="collapse" data-bs-target="#movements_id">
                    Show Movements
                </button>
            </span>
            <span class="pe-2">
                <button id="show_credits_button" type="btn" class="btn btn-outline-secondary" data-bs-toggle="collapse" data-bs-target="#credits_id">
                    Show All Credits
                </button>
            </span>
        </div>

For example,

<tr class="song_id collapse" id="movements_id">
    <td class="col-1">
        1
    </td>
    <td class="col">
    </td>
    <td class="col">
        <div>
            <label class="h6">
                Piano Concerto no. 1 in E minor, op. 11: I. Allegro maestoso
            </label>
        </div>
        <div class="collapse" id="credits_id">
            <div class="lh-1">
                <div>
                    <a href="/container.start?cid=0$=Instrument$708&amp;title=Instruments+%2F+piano" class="small text-secondary">
                        piano
                    </a>
                    <label class="small">
                         by 
                    </label>
                    <a href="/container.start?cid=0$=Performer_name$5540&amp;title=Performers+%2F+Evgeny+Kissin" class="small text-secondary pe-1">
                        Evgeny Kissin
                    </a>
                </div>
            </div>
        </div>
    </td>
</tr>

The existing code works as intended, but I aim to dynamically change the button text based on whether it is showing or hiding content. To achieve this, I have added the following additional code:

<script>

function listenForButtonCollapse(buttonId, collapseId, buttonShowText, buttonHideText)
{
    let button  = document.getElementById(buttonId);
    let section = document.getElementById(collapseId);
    if(section!=null)
    {
        section.addEventListener('show.bs.collapse',
            function()
            {
                button.innerText=buttonHideText;
            }
        );

        section.addEventListener('hide.bs.collapse',
            function()
            {
                button.innerText=buttonShowText;
            }
        );
    }
}
  </script>
  <script>
       listenForButtonCollapse('show_credits_button','credits_id','Show All Credits','Hide Some Credits');
  </script>
  <script>
      listenForButtonCollapse('show_movements_button','movements_id','Show Movements','Hide Movements');
  </script>      

While toggling the Show/Hide Movements button functions correctly, clicking on the Show/Hide Credits button unintentionally triggers the listenForButtonCollapse() call for both buttons. This causes the Movement button to reflect the same (Hide/Show) value as the credits button, even though it should not affect the movements div visibility.

Given that the credits div is nested within the movements div, I suspect this nesting may be causing the issue. However, I am unable to identify exactly what mistake I am making in my implementation.

Answer №1

To prevent the current behavior, it is necessary to halt the event propagation. This can be achieved by utilizing the stopPropagation() method. By using this method from the Event interface, you can effectively cease any further propagation of the ongoing event in both the capturing and bubbling phases.

function handleButtonCollapse(buttonId, collapseId, showText, hideText)
{
    let button  = document.getElementById(buttonId);
    let section = document.getElementById(collapseId);
    
    if (section != null)
    {
        section.addEventListener('show.bs.collapse',
            function(event)
            {
                event.stopPropagation();
                button.innerText=hideText;
            }
        );

        section.addEventListener('hide.bs.collapse',
            function(event)
            {
                event.stopPropagation();
                button.innerText=showText;
            }
        );
    }
}

A Code playground has been set up to demonstrate how this approach resolves your issue:

https://codesandbox.io/embed/bootstrap-5-playground-forked-b28j0g?fontsize=14&hidenavigation=1&theme=dark

Explanation of Solution

The events show.bs.collapse and hide.bs.collapse are triggered for both buttons upon clicking. When multiple event listeners are added for these events, all registered listeners get executed when the event occurs. For instance, when clicking on "Hide Movements" and bootstrap triggers the hide.bs.collapse event, it executes all registered listeners - in this case two listeners, causing both button texts to change. To prevent this, stopping further event propagation with event.stopPropagation() is essential as it restricts other event listeners from being notified except for the target event listener.

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

Adjust the height of a div based on the font size and number of lines

I'm trying to create a function that automatically sets the height of a div by counting lines. I managed to get it partially working, but then it stopped. Can someone please help me with this? function set_height() { var div_obj=document.getEleme ...

Using Javascript to retrieve a variable and dynamically updating the source of an HTML iframe

I have two JavaScript variables, 'long' and 'lat', in the code below. My challenge is to append these values to the end of the iframe URL. I would appreciate assistance on modifying the code below to achieve this. The iframe code bel ...

Add a fresh category to a JSON document

Combining Express (Node.js) and Mongoose, I am developing a REST API and attempting to implement JWT token-based login. However, I have encountered an issue. Upon executing the code below: const mongoose = require('mongoose'); const User = mongo ...

Attempting to retrieve JSON data using the subscribe method in Angular version 7.x

In my Angular 7.x application, I have a component that calls a function from a service. This function makes a GET request to a backend endpoint and retrieves an array of users. Although I can view the data within the subscribe method (where console.log is ...

JavaScript form validation involves using client-side scripting to ensure that

I'm currently working on form validation, but I'm unsure if I'm overcomplicating things. I have a variable that compares elements and replaces an image with a checkmark for success or an X for failure upon successful validation. The function ...

Utilize the synchronization feature of ES6 Promises in Jasmine with the then/catch method

I have an angular controller that needs to be tested. This controller utilizes a service to fetch data from a server, and the service returns ES6 Promises. function MyController($scope, MyService) { $scope.doSomething = function () { MyService.foo() ...

Fade background when the youtube video in the iframe begins playing

Hey there! I'm looking to create a cool effect on my (wordpress) site where the background dims when a YouTube video starts playing. The video is embedded in this way: <iframe frameborder="0" width="660" height="371" allowfullscreen="" src="https ...

Develop a precompiled library for Angular applications that utilizes Ahead-of-Time (AOT) compilation technology

My Angular 5 library is packaged for consumption by other apps in their node_modules. Currently, the app is compiled Just-in-Time(JIT) using rollup and gulp. I export it as a package, and developers use it in its JIT compiled form. After researching AOT ...

Engage the PROTRACTOR refresh function

I'm just getting started with automation and Protractor. I successfully automated the login page, but now I'm facing a challenge with accessing a menu that I need in order to navigate to a different page. Within the menu, there is an href="#/dom ...

What is the best way to generate a fresh JSON object within a react hook function?

I am currently facing two issues. Firstly, I am having trouble figuring out how to add/update the JSON items within a hook. Secondly, React seems to be restricting me from using the name that is stored in a previous JSON file. I am open to exploring alter ...

Issue with fullcalendar: difficulty displaying events using ajax after clicking 'previous' or 'next' button

I am currently working on creating a calendar using fullcalendar. To retrieve data for the month, I make an external ajax request. Here are the key variables I utilize to render the fullcalendar: eventsJsonArray - used to load all events for the month ...

Looking for a Search Field that clears default text when focused - Compatible with Firefox 3.6?

I have attempted various jQuery and JavaScript solutions found on this page: How to clear text field on focus of text field Surprisingly, all of the solutions work except for FF 3.6. So, I am wondering, What alternative can I use to make this feature com ...

Issues with Contenteditable functionality in JavaScript

My goal is to make a row editable when a button is clicked. $(":button").click(function(){ var tdvar=$(this).parent('tr').find('td'); $.each(tdvar,function(){ $(this).prop('contenteditable',true); }); }); <s ...

Is it possible to merge a variable within single quotes in XPath?

Currently working with nodeJS and experimenting with the following code snippet: for (let i = 1; i <= elSize; i++) { try { let DeviceName = await driver .findElement(By.xpath("//span[@class='a-size-medium a-color-base a-text-normal ...

Leveraging Cheerio in Node.js to locate a precise value within an option tag

I'm facing difficulties in selecting the exact number (in this case 7) which is the value of the option. This is what I'm attempting: var $ = cheerio.load(html); console.log($('ProductSelect').val($("option:contains('7')").v ...

Retrieving the initial item from a Response.Json() object

i have a this code: fetch("https://rickandmortyapi.com/api/character/?name=Rick") .then((response) => { response.json().then((data) => { console.log(JSON.stringify(data)) }).catch( (error) => { console.log(`Error: $ ...

Error: Unable to set attribute because the property is undefined in the onLoad function

Can anyone help troubleshoot this error? List of included files: <link rel="stylesheet" href="../../node_modules/semantic-ui/dist/semantic.min.css"> <link rel="stylesheet" href="../../node_modules/font-awesome/css/font-awesome.min.css"> <l ...

The information window is malfunctioning on Google Maps

I created buttons that are linked to specific locations on a map and they seem to be functioning, although not in the most efficient way. However, when attempting to add an info window to appear on the marker, it does not work as expected. I am unsure of ...

What is the best way to utilize Link for navigation in React with Material UI?

const pages = ['home', 'menu', 'reservation']; <Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}> {pages.map((page) => ( <Link component={Link} to='/'>Home</L ...

Using the for-each loop in Express.js with Node

I'm currently working on developing a REST API using express with Node. I have a requirement to iterate through a for loop in order to generate the desired JSON output. Here is a snippet of my route file: var Redis = require('ioredis') var ...