2

I am trying to build reusable dropdown with latest jQuery 3.6.1 and Bootstrap 5.2.3 but it has proven difficult.

I need to dynamically update the dropdown's links with values from data attributes specified on the button that was clicked.

I am looking at the excellent solution provided by roko-c-buljan but it is using vanilla js and I need jQuery. Below is my code by clicking on the Action buttons "...", I get an error:

{
  "message": "Uncaught TypeError: Cannot read properties of null (reading 'classList')",
  "filename": "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.bundle.min.js",
  "lineno": 6,
  "colno": 44054
}

Code

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css" integrity="sha512-SbiR/eusphKoMVVXysTKG/7VseWii+Y3FdHrt0EpKgpToZeemhqHeZeLWLhJutz/2ut2Vw1uQEj2MbRF+TVBUA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
</head>
<body>
    <table class="table">
        <thead>
            <tr>
                <th>Type</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>item 1</td>
                <td>
                    <div class="dropdown">
                        <button type="button" class="btn btn-primary" data-type="ddbtn" data-id="1"  data-value="some value 1" data-bs-toggle="dropdown" aria-expanded="false" >...</button>
                    </div>
                </td>
            </tr>
            <tr>
                <td>item 2</td>
                <td>
                    <div class="dropdown">
                        <button type="button" class="btn btn-primary" data-type="ddbtn" data-id="2" data-value="some value 2" data-bs-toggle="dropdown" aria-expanded="false" >...</button>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>

    <ul id="contextMenu" class="dropdown-menu">
        <li><a href="#" tabindex="-1" class="dropdown-item" id="link1">Action 1</a></li>
        <li><a href="#" tabindex="-1" class="dropdown-item" id="link2">Action 2</a></li>
    </ul>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.bundle.min.js" integrity="sha512-i9cEfJwUwViEPFKdC1enz4ZRGBj8YQo6QByFTF92YXHi7waCqyexvRD75S5NVTsSiTv7rKWqG9Y5eFxmRsOn0A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
    $(function () {
        $dropdown = $("#contextMenu"); // let's get the dropdown menu
        $("button[data-type='ddbtn']").click(function () {
            var id = $(this).data("id");
            console.log(id);

            $("#link1").attr("href","/action/dosomething?id=" + id);
            $("#link1").text($(this).data("value")); // just for test purposes.
            $("#link2").attr("href","/action/somethingelse?id=" + id);


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

0

2 Answers 2

1

Try this

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css" integrity="sha512-SbiR/eusphKoMVVXysTKG/7VseWii+Y3FdHrt0EpKgpToZeemhqHeZeLWLhJutz/2ut2Vw1uQEj2MbRF+TVBUA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.bundle.min.js" integrity="sha512-i9cEfJwUwViEPFKdC1enz4ZRGBj8YQo6QByFTF92YXHi7waCqyexvRD75S5NVTsSiTv7rKWqG9Y5eFxmRsOn0A==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
    <table class="table">
        <thead>
            <tr>
                <th>Type</th>
                <th>Action</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>item 1</td>
                <td>
                    <div class="dropdown">
                        <button type="button" class="btn btn-primary" data-type="ddbtn" data-id="1"  data-value="some value 1" data-bs-toggle="dropdown" aria-expanded="false" >...</button>
                    </div>
                </td>
            </tr>
            <tr>
                <td>item 2</td>
                <td>
                    <div class="dropdown">
                        <button type="button" class="btn btn-primary" data-type="ddbtn" data-id="2" data-value="some value 2" data-bs-toggle="dropdown" aria-expanded="false" >...</button>
                    </div>
                </td>
            </tr>
        </tbody>
    </table>
    <ul id="contextMenu" class="dropdown-menu">
        <li><a href="#" tabindex="-1" class="dropdown-item link1">Action 1</a></li>
        <li><a href="#" tabindex="-1" class="dropdown-item link2">Action 2</a></li>
    </ul>

    <script>
    $(function () {
        var $dropdown = $("#contextMenu");

        $("button[data-type='ddbtn']").each(function(){
            var id = $(this).data("id");
            var $menu = $dropdown.clone();

            $menu.find(".link1").attr("href","/action/dosomething?id=" + id);
            $menu.find(".link1").text($(this).data("value"));
            $menu.find(".link2").attr("href","/action/somethingelse?id=" + id);
            $(this).after($menu);
        });
    });
    </script>
</body>
</html>

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

Comments

1

Using the first solutions of this related answer in vanilla JavaScript:
simply assign a "shown.bs.dropdown" handler to every button and use dataset to get the data attribute values. Once you got your data values (by using const {id, value} = elBtn.dataset), use setAttribute() or textContent = to set the appropriate changes to your dropdown's Anchor elements

// DOM utility functions:
const el = (sel, par) => (par || document).querySelector(sel);
const els = (sel, par) => (par || document).querySelectorAll(sel);

// Task: BS5 Popper fix for single static dropdown menu:
const elDropdown = el('#contextMenu');
const elsBtns = els(".optionsButton");
const elDDLink1 = el("#link1");
const elDDLink2 = el("#link2");

const dropdownList = [...elsBtns].map((elBtn) => {
  const instance = new bootstrap.Dropdown(elBtn);
  instance._menu = elDropdown;

  // Add listener to clicked button calling dropdown,
  // and perform some dynamic changes on the dropdown's anchors:
  elBtn.addEventListener("shown.bs.dropdown", (evt) => {
    const {
      id,
      value
    } = elBtn.dataset;
    elDDLink1.setAttribute("href", "/action/dosomething?id=" + id);
    elDDLink1.textContent = value; // just for test purposes.
    elDDLink2.setAttribute("href", "/action/somethingelse?id=" + id);
  });

  return instance;
});
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">


<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">
          <button type="button" class="btn btn-primary optionsButton" data-type="ddbtn" data-id="1" data-value="some value 1" data-bs-toggle="dropdown" aria-expanded="false">...</button>
        </div>
      </td>
    </tr>
    <tr>
      <td>2</td>
      <td>Training Material</td>
      <td>GP-02-2022</td>
      <td>
        <div class="dropdown">
          <button type="button" class="btn btn-primary optionsButton" data-type="ddbtn" data-id="2" data-value="some value 2" data-bs-toggle="dropdown" aria-expanded="false">...</button>
        </div>
      </td>
    </tr>
  </tbody>
</table>


<ul id="contextMenu" class="dropdown-menu">
  <li><a href="#" tabindex="-1" class="dropdown-item" id="link1">Action 1</a></li>
  <li><a href="#" tabindex="-1" class="dropdown-item" id="link2">Action 2</a></li>
</ul>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>

3 Comments

Elegant solution also albeit not in jQuery. I have one problem with this solution. You cannot open another dropdown if a dropdown is already visible. Can this behavior be avoided?
@Sha multiple opened popups/dropdowns are not a good UI/UX. Also, you don't need jQuery. Don't be afraid to use JavaScript.
I know re multiple opened dropdowns but that is not the challenge. If you have one dropdown, you can't click on another - you first have to click somewhere outside the dropdown area before the second dropdown can be opened. Essentially the user has to click twice which is not good UX.

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.