javascripthtmlmaterial-designmaterial-design-lite

MDL upgrade all dynamic list elements on each tab


I have 4 mdl tabs where I dynamically create a list of mdl cards in each one. Each card has a mdl-menu created like this:

// job settings dropdown :
settingsButton.setAttribute('id', 'jobSettings');
var jobUl = document.createElement('ul');
jobUl.className = 'mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect';
jobUl.setAttribute('for', 'jobSettings');
var jobLi = document.createElement('li');
var jobLi2 = document.createElement('li');
var jobLi3 = document.createElement('li');
jobLi.className = 'mdl-menu__item';
jobLi2.className = 'mdl-menu__item';
jobLi3.className = 'mdl-menu__item';
jobLi.textContent = 'Edit';
jobLi2.textContent = 'Delete';
jobLi3.textContent = 'Pay';
jobUl.appendChild(jobLi);
jobUl.appendChild(jobLi2);
jobUl.appendChild(jobLi3);

Which would show up in html something like this:

<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect"
    for="demo-menu-lower-right">
  <li class="mdl-menu__item">Edit</li>
  <li class="mdl-menu__item">Delete</li>
  <li class="mdl-menu__item">Pay</li>

Then I do componentHandler.upgradeDom(); hoping to upgrade the mdl-menu items in each unordered list (ul).

This only seems to work for ONE list element (li) in the entire website. it doesn't work for any other element on any other tab. How do I make my mdl-menu work on each dynamically created list element in each dynamically created ul?


Solution

  • The issue could be that you are duplicating the id value across multiple menu elements (MDL will get confused if there is more than one button with an id value that matches the for value on your menu element). You could add a numerical increment to your id and for values to ensure that each menu is uniquely identified. See the following example.

    const fragment = document.createDocumentFragment();
    const addCard = (n) => {
      const card = document.createElement('div');
      const menu = document.createElement('div');
      const button = document.createElement('button');
      const icon = document.createElement('i');
      const ul = document.createElement('ul');
      card.className = 'mdl-card mdl-shadow--2dp';
      menu.className = 'mdl-card__menu';
      button.id = `menu${n}`;
      button.className = 'mdl-button mdl-js-button mdl-button--icon';
      icon.className = 'material-icons';
      icon.textContent = 'more_vert';
      ul.className = 'mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect';
      ul.setAttribute('for', `menu${n}`);
      fragment.appendChild(card);
      card.appendChild(menu);
      menu.appendChild(button);
      button.appendChild(icon);
      menu.appendChild(ul);
      for (const action of ['Action 1', 'Action 2', 'Action 3']) {
        const li = document.createElement('li');
        li.textContent = action;
        li.className = 'mdl-menu__item';
        ul.appendChild(li);
      }
    };
    
    for (let i = 0; i < 2; i++) {
      addCard(i);
    }
    
    document.querySelector('#container').appendChild(fragment);
    componentHandler.upgradeDom();
    .mdl-card {
      margin: 8px;
    }
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Material Design Lite Cards / Menus</title>
        <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
        <link rel="stylesheet" href="https://code.getmdl.io/1.3.0/material.indigo-pink.min.css">
      </head>
      <body>
        <div id="container"></div>    
        <script src="https://code.getmdl.io/1.3.0/material.min.js"></script>
      </body>
    </html>