Having trouble with Bootstrap dropdowns not opening when clicking with jQuery?

I am in the process of developing a table with multiple rows, each containing an "Options" button to display a dropdown context menu. To streamline the code, I am utilizing a single div to serve as a common markup for the context menu.

The technologies I am using include Bootstrap 5.1.3 and jQuery 3.6.0. Below is the snippet of my code:

<!doctype html>
<html lang="en>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Test Code</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</head>

<body>
  <table id="myTable" class="table table-hover">
    <thead>
      <tr>
        <th>#</th>
        <th>Document</th>
        <th>Reference</th>
        <th>Action</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>1</td>
        <td>General Policies</td>
        <td>GP-01-2022</td>
        <td>
          <div class="dropdown">
            <a href="#" class="btn btn-primary optionsButton" data-bs-toggle="dropdown" aria-expanded="false" id="doc1">Options</a>
          </div>
        </td>
      </tr>
      <tr>
        <td>2</td>
        <td>Training Material</td>
        <td>GP-02-2022</td>
        <td>
          <div class="dropdown">
            <a href="#" class="btn btn-primary optionsButton" data-bs-toggle="dropdown" aria-expanded="false" id="doc2">Options</a>
          </div>
        </td>
      </tr>
    </tbody>
  </table>

  <ul id="contextMenu" class="dropdown-menu">
    <li><a tabindex="-1" href="#" class="dropdown-item downloadLink">Download</a></li>
    <li><a tabindex="-1" href="#" class="dropdown-item propertiesLink">Properties</a></li>
  </ul>

  <script>
    //save the selector so you don't have to do the lookup everytime
    var $dropdown = $('#contextMenu');

    $('.optionsButton').click(function(event) {

      //get document ID
      var id = this.id;

      //move dropdown menu
      $(this).after($dropdown);

      //update links
      $dropdown.find(".downloadLink").attr("href", "/data/download?id=" + id);
      $dropdown.find(".propertiesLink").attr("href", "/data/viewproperties?id=" + id);

      //show dropdown
      $(this).dropdown();
    });
  </script>


</body>

</html>

While working on this code, I am encountering two specific issues. Firstly, the dropdown menu fails to open. Upon inspection in Developer Mode, I can see that the jQuery script correctly positions the contextmenu DIV beneath the "Options" button as required by Bootstrap. However, the $(this).dropdown(); call does not trigger the menu to open.

The second problem arises every time I click the 'Options' button, an error is logged in the Developer Mode console:

dropdown.js:285 Uncaught TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.

The stack trace for this error references dropdown.js, without pinpointing the exact source of the issue in my code.
I am seeking assistance in diagnosing and resolving these issues. As someone relatively new to Bootstrap and jQuery, I would appreciate any guidance. Thank you.

Answer №1

Summary: Avoid relocating the #contextMenu. For the solution, refer to: Resolution*


The encountered error

dropdown.js:285 Uncaught TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.

is linked to the Bootstrap (v5.1.3): dropdown.js code:

// @l62:
const SELECTOR_MENU = '.dropdown-menu'

// @l103:
  constructor(element, config) {
    super(element);
    //...
    this._menu = this._getMenuElement()
    //...
  }

// @l269:
  _getMenuElement() {
    return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
  }

// @l285:
   _getPlacement() {
    // ...
    const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'
    // ...
  }

here:

SelectorEngine.next(this._element, 

as you can see, there's no way to pass to the constructor another Element .menu-dropdown besides the one that BS hardcoded, and that's a next Sibling element in the Method _getMenuElement() ~line285.

BS assigns a "click" Event to every button with

data-bs-toggle="dropdown"
and blindly expects to toggle a next sibling Element dropdown — which does not actually exists (yet)!

I would either:

  • extend the class Dropdown manually, or
  • raise an Issue and create a pull-request to the related Bootstrap module

as a way to pass any desired Element as the this._menu; something like:

  constructor(element, config) {
    //...
    // Fix: allow for custom reusable menu Element
    this._menu = config.menuElement || this._getMenuElement()
    //...
  }

Note: There have been some modifications in the main branch related to the previously mentioned code, the resolution to these issues is uncertain as of now.


In the meantime, instead of utilizing the "mousedown" Event or the problematic Event.stopPropagation(), you can:

Resolution:

Avoid moving the UL#contextMenu using .after() or (with JS) .insertAdjacentElement(). Instead, modify the expected Bootstrap this._menu property to refer to the desired reusable Element on Popper instance creation — your in-body "#contextMenu" as follows:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Test Code</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
...

The benefit of this approach is the absence of DOM changes that trigger a reflow. The Popper code will determine the optimal position of your floating contextMenu without any disruptions. However, extra caution is needed when dynamically adding TR elements to the Table to ensure proper instantiation of new Buttons as new bootstrap.Dropdown(elBtn).

Using "mousedown"

Another option, albeit less ideal, is to (needlessly) move the dropdown in the DOM using the "mousedown" Event. Nonetheless, this method might result in visual glitches and is not recommended. Here's a sample code snippet:

<!doctype html>
<html lang="en">

<head>
  <meta charset="utf-8>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Test Code</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
...

PS: It's advisable to use

<button type="button">
over Anchors when no navigation is intended. Bootstrap's implementation of popups and selects may have flaws, and contributing through a Pull Request could enhance the framework.


For a guide on creating a similar popup from scratch using JavaScript, refer to: Show custom popup on mouse location.

Answer №2

I made some modifications to the JavaScript in your code by focusing on the dropdown functionality. The logic to display the dropdown was simplified to $dropdown.show() instead of $(this).dropdown(); since the dropdown is already being tracked by the variable.

Additionally, I added a toggle effect where the dropdown can be hidden in three scenarios:

  • Clicking elsewhere in the table
  • Clicking on the same button
  • Clicking on a similar button in a different row, which will move the dropdown to the appropriate location with the necessary parameters

<!doctype html>
<html lang="en>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Test Code</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="13717c7c67606761726353263d223d20">[email protected]</a>/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="84e6ebebf0f7f0f6e5f4c4b1aab5aab7">[email protected]</a>/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</head>
<body>
    <table id="myTable" class="table table-hover" onclick="hideall()">
        <thead>
            <tr>
                <th>#</th>
                <th>Document</th>
                <th>Reference</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>General Policies</td>
                <td>GP-01-2022</td>
                <td>
                    <div class="dropdown">
                        <a href="#" class="btn btn-primary optionsButton" aria-expanded="false" id="doc1">
                            Options
                        </a>
                    </div>
                </td>
            </tr>
            <tr>
                <td>2</td>
                <td>Training Material</td>
                <td>GP-02-2022</td>
                <td>
                    <div class="dropdown">
                        <a href="#" class="btn btn-primary optionsButton" aria-expanded="false" id="doc2">
                            Options
                        </a>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
    
    <ul id="contextMenu" class="dropdown-menu">
        <li><a tabindex="-1" href="#" class="dropdown-item downloadLink">Download</a></li>
        <li><a tabindex="-1" href="#" class="dropdown-item propertiesLink">Properties</a></li>
    </ul>
    
    <script>
        // Save selector to avoid lookup for dropdown menu
        var $dropdown = $('#contextMenu');
        var activeOptionsButton;

        $('.optionsButton').click(function(event) {
            event.stopPropagation();
            if (activeOptionsButton === this) {
                hideall();
                activeOptionsButton = undefined;
                return;
            }
            activeOptionsButton = this;
            var id = this.id;
            
            $(this).after($dropdown);

            $dropdown.find(".downloadLink").attr("href", "/data/download?id="+id);
            $dropdown.find(".propertiesLink").attr("href", "/data/viewproperties?id="+id);
            
            $dropdown.show();
        });
        
        function hideall() {
            $dropdown.hide();
        }
    
    </script>
</body>
</html>

Answer №3

give this a shot..

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Tryout Code</title>
  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="05676a6a71767177647545312b332b34">[email protected]</a>/styles/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3953484c5c4b40790a170c1708">[email protected]</a>/modules/js/jquery.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="4f3f203f3f2a3d61253c0f7e617e79617e">[email protected]</a>/bundles/js/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"gt;</script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="3c5e5353484f484e5d4c7c08120a120d">[email protected]</a>/modules/js/bootstrap.min.js" integrity="sha384-VHvPCCyXqtD5DqJeNxl2dtTyhF78xXNXdkwX1CZeRusQfRKp+tA7hAShOK/B/fQ2" crossorigin="anonymous"></script>
  
</head>
<body>
    <table id="myTable" class="table table-hover">
        <thead>
            <tr>
                <th>#</th>
                <th>Document</th>
                <th>Reference</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>1</td>
                <td>General Policies</td>
                <td>GP-01-2022</td>
                <td>
                    <div class="dropdown">
                        <a href="#" class="btn btn-primary optionsButton" data-bs-toggle="dropdown" aria-expanded="false" id="doc1">
                            Options
                        </a>
                    </div>
                </td>
            </tr>
            <tr>
                <td>2</td>
                <td>Training Materials</td>
                <td>GP-02-2022</td>
                <td>
                    <div class="dropdown">
                        <a href="#" class="btn btn-primary optionsButton" data-bs-toggle="dropdown" aria-expanded="false" id="doc2">
                            Options
                        </a>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
    


    <ul id="contextMenu" class="dropdown-menu">
        <li><a tabindex="-1" href="#" class="dropdown-item downloadLink">Download</a></li>
        <li><a tabindex="-1" href="#" class="dropdown-item propertiesLink">Properties</a></li>
    </ul>
    
    <script>
        //cache the selector for optimization
        var $dropdown = $('#contextMenu');

        $('.optionsButton').click(function(event) {
            
            //retrieve document ID
            var id = this.id;
            
            //reposition dropdown menu
            $(this).after($dropdown);

            //update links
            $dropdown.find(".downloadLink").attr("href", "/data/download?id="+id);
            $dropdown.find(".propertiesLink").attr("href", "/data/viewproperties?id="+id);
            
            //display the dropdown
            $(this).dropdown();
        });
    
    </script>


</body>
</html>

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

Guide to creating a synchronous wrapper for jQuery ajax methods

I've made the decision to switch from synchronous ajax calls to asynchronous ones due to lack of support in most modern browsers. My code is currently reliant on synchronous (and dynamic) ajax calls to client-side functions that must be completed befo ...

Mobile devices do not support HTML5 Video playback

Here is the HTML5 Video code I am using: <div id="lightBox1" class="lightBox"> <video id="video" controls preload="metadata"> <source width="100%" height="470" src="/ImageworkzAsia/video/iworkzvid.mp4" type="video/mp4"> ...

Dynamic Website (Link Relationships / Responsive Design / Content Rendering with CSS and HTML)

Hello everyone! My name is Mauro Cordeiro, and I come from Brazil. I am just diving into the world of coding and have been following various tutorials. Stack Overflow has been an invaluable resource for me in my learning journey! Aside from coding, I am a ...

Upon selecting the checkbox, the user will be redirected to an input textbox

Can I make it so that when I check a checkbox, it automatically directs me to a textbox as if I pressed the TAB key? Is there a way to achieve this using only HTML and CSS? ...

When a radio button is checked, add a class to its parent element that has a specific class assigned to it

In order to dynamically add a class to a specific div element higher up the DOM hierarchy when a radio button is clicked, I am in need of assistance. There are multiple instances of these div elements with different radio buttons, so it is crucial that on ...

Switch between Fixed and Relative positions as you scroll

Just a small adjustment is needed to get it working... I need to toggle between fixed and relative positioning. JSFiddle Link $(window).on('scroll', function() { ($(window).scrollTop() > 50) ? ($('.me').addClass('fix ...

What is the best way to incorporate a percentage-based width scrollbar?

Help needed: I want to implement a scroll feature for the parent div but here's the catch - the width of the text inside the div needs to be in percentage. So, if there is a long name, it should scroll within the parent div. HTML: <div id="list"& ...

Are max-width:auto and max-width:100% the same thing?

Does max-width set to auto have the same result as setting max-width to 100% If not, what is the distinction between them? ...

Mobile devices do not trigger the initialization of jQuery animation

I've been working on adjusting the opacity of a class using the jQuery hover function, without utilizing the <script> tag. <script src="http://code.jquery.com/mobile/1.2.0/jquery.mobile-1.2.0.min.js"> </script> Below is the code I ...

Find the elements of <a> tags specifically without the attribute href

Currently, I am extracting the class of all <a> elements in an HTML document of a webpage using VB.net from a WinForm: Dim htmlLinks As HtmlElementCollection = WebBrowser1.Document.GetElementsByTagName("a") For Each link As HtmlElement In htmlLi ...

Calculator built with HTML, CSS, and JavaScript

Hi there, I'm experiencing some issues with my calculator. The buttons seem to be working fine and lining up correctly, but for some reason, nothing is showing up on the monitor or getting calculated when I press the buttons. Here's the code that ...

Create a webpage that utilizes PHP, MySQL, and HTML to load additional content in a way similar to Facebook or the

Seeking guidance on how to incorporate pagination functionality akin to Twitter and Facebook, where a list of items loads initially and then a "load more" button appears below. When clicked, this button appends the list with additional items. Can anyone ...

Positioning Bootstrap table in the middle of the screen

I need help figuring out how to keep my bootstrap table centered in the middle of the page, instead of adjusting based on the tab selected within a bootstrap nav tab. Currently, it centers around the active tab, but I want it to stay in the middle no matte ...

Prestashop 1.7 - Enhanced top menu navigation with drop-down subcategories

While using the ps_mainmenu top menu with the default Prestashop theme, I noticed that the drop-down menu only works for parent categories and not subcategories. You can see this in the attached image and HTML code from Chrome. I'm looking for a way t ...

Ensure that the elements are properly arranged inside divs that have been stretched

My goal is to create divs that are all the same size, while aligning some content within each div differently due to varying lengths. The objective is to keep the icon and text in a consistent position within all divs, but allow the divs to expand at the ...

Catching exceptions with jQuery Ajax

I'm facing a tricky issue with an exception that seems to slip through my fingers: //myScript.js.coffee try $.ajax async: false type: "GET" url: index_url success: -> //Do something error: -> //Do something els ...

dynamically adjust table cell width based on number of rows

My HTML structure is as follows: <table border=1 > <tr> <!--this tr has one <td> that needs to be 100% width--> <td></td> </tr> <tr> <!--this tr has two <td> that each need to be ...

Footer refuses to stay anchored at the bottom of the page

I'm struggling to keep my footer at the bottom of all pages on my blog. I am facing two main issues. If I use position:absolute, the footer ends up in the middle of the main blog page. Alternatively, when I don't use it, the footer sticks to the ...

Using PHP to display dynamic data and add it to an HTML element

I have a unique situation where I am storing html elements, specifically <rect> svg elements, as strings in a database. In my PHP code, I retrieve and echo this data: $db = getConnection(); $statement = $db->prepare('SELECT `copy` FROM `dra ...

Having trouble changing a CSS property (margin-right)?

I have created a simple HTML list that consists of various items: <div id="menu"> <ul> <li>apples</li> <li>&#149</li> <li>bananas</li> <li>oranges</li> <li>grape ...