Home > other >  Bootstrap - how to access an item out of 1000 items via 3 level hierarchy?
Bootstrap - how to access an item out of 1000 items via 3 level hierarchy?

Time:09-19

A user can select an item from say a 1000 items. To access an item, first you select from a list of 60 items. Then a dynamic list is created for that item. You select one sub item. In some cases that is enough. In other cases, the user can select from another dynamic list.

The level 2 and 3 items are short, e.g. just numbers, letters or sizes.

Boostrap is available, so can be used.

How can I create such a multi-level search that is intuitive, easy, usable at mobiles?

Solution 1 might be a Bootstrap multi-level dropdown menu. When you first select item 50, then the second menu might not be easy accessible. Some code you find below.

enter image description here

Solution 2 is like solution 1, but the number of visible rows per menu is limited. This one is not working, because horizontally scrolling does not work (yet).

<ul >
  <li><a  href="#"> Dropdown item 1 </a></li>
  <li><a  href="#"> Dropdown item 1a </a></li>
  <li><a  href="#"> Dropdown item 1b </a></li>
  ..... many rows
  <li><a  href="#"> Dropdown item 10 &raquo; </a>
     <ul >
      <li><a  href="#">Submenu item 1</a></li>
      <li><a  href="#">Submenu item 2</a></li>
      <li><a  href="#">Submenu item 3 &raquo; </a>
        <ul >
          <li><a  href="#">Multi level 1</a></li>
          <li><a  href="#">Multi level 2</a></li>
      </ul>
      </li>
      <li><a  href="#">Submenu item 4</a></li>
      <li><a  href="#">Submenu item 5</a></li>
   </ul>
  </li>
</ul>

The style sheet contains:

.menu-scroll {
    overflow-y: scroll;
    max-height: 200px;
}

Solution 2-B: Adding the new level-2 of items dynamically below the selected level 1 is not a good idea. This may prevents seeing clearly the level 1 selection. Even when selecting the level 3 items, the selected level-1 and level-2 items should be visible.

Solution 3 might be having a dropdown. A selection fills dynamically a second dropdown. When selecting an item might trigger the filling of a third dropdown. Could be a solution. Does anyone has an example? I was not able to find a good solution yet.

Solution 4 could be a drop down for the level 1 items. Selecting one item could show the second level as a series of small buttons. In this case numbers or letters. Selecting such a button could reveal a new set of buttons below.

enter image description here

Solution 5 is a enhanced search box. That is available not a very good solution.

Can you give me some hints on solving this puzzle? Bootstrap is available.

An example of the multi level menu is given below:

<ul>    
  <li >
    <a  href="#" data-bs-toggle="dropdown">  Treeview menu  </a>
      <ul >
      <li><a  href="#"> Dropdown item 1 </a></li>
      <li><a  href="#"> Dropdown item 2 &raquo; </a>
         <ul >
          <li><a  href="#">Submenu item 1</a></li>
          <li><a  href="#">Submenu item 3 &raquo; </a>
            <ul >
              <li><a  href="#">Multi level 1</a></li>
              <li><a  href="#">Multi level 2</a></li>
          </ul>
          </li>
          <li><a  href="#">Submenu item 4</a></li>
       </ul>
      </li>
      <li><a  href="#"> Dropdown item 3 </a></li>
      </ul>
  </li>
  <li >
    <a  href="#" data-bs-toggle="dropdown">  More items  </a>
      <ul >
      <li><a  href="#"> Dropdown item 1 </a></li>
      <li><a  href="#"> Dropdown item 2 &raquo; </a>
         <ul >
          <li><a  href="#">Submenu item 1</a></li>
       </ul>
      </li>
      <li><a  href="#"> Dropdown item 3 &raquo; </a>
         <ul >
          <li><a  href="#">Another submenu 1</a></li>
       </ul>
      </li>
      <li><a  href="#"> Dropdown item 5 </a></li>
    </ul>
  </li>
</ul>

When level 1 is selected, that one should be visible. Then the level 2 items are shown. On selection of the level 2 item, a visible cue shows that selection and reveals a third level. So on selecting a new level item, the previous selected items should be high-lighted or so.

CodePudding user response:

I believe you can make it dynamic:

function generateDropdown(data) {
    let template = ``;
    for (let key in data) {
        if (typeof(data[key]) === "object") {
            template  = `<li onclick="select(this); event.stopPropagation();">${key}: ${generateDropdown(data[key])}</li>`;
        } else {
            template  = `<li onclick="select(this); event.stopPropagation();">${key}: ${data[key]}</li>`;
        }
    }
    return `<ul>${template}</ul>`;
}

function select(what) {
    if (what.classList.contains("selected")) {
        what.classList.remove("selected");
        while (!what.classList.contains("multiselect-dropdown")) {
            what = what.parentNode;
            if (what.classList.contains("opened")) {
                what.classList.remove("opened");
                what.classList.add("selected");
                return;
            }
        }
        return;
    } else if (what.classList.contains("opened")) {
       for (let subitem of what.querySelectorAll(".selected, .opened")) {
           if (subitem.classList.contains("selected")) subitem.classList.remove("selected");
           if (subitem.classList.contains("opened")) subitem.classList.remove("opened");
       }
       what.classList.remove("opened");
        while (!what.classList.contains("multiselect-dropdown")) {
            what = what.parentNode;
            if (what.classList.contains("opened")) {
                what.classList.remove("opened");
                what.classList.add("selected");
                return;
            }
        }
        return;
    }
    let root = what;
    while (!root.classList.contains("multiselect-dropdown")) root = root.parentNode;
    let selected = root.querySelectorAll(".selected");
    for (let s of selected) s.classList.remove("selected");
    let opened = root.querySelectorAll(".opened");
    for (let o of opened) o.classList.remove("opened");
    what.classList.add("selected");
    root = what.parentNode;
    while (!root.classList.contains("multiselect-dropdown")) {
        if (root.tagName.toLowerCase() === "li") root.classList.add("opened");
        root = root.parentNode;
    }
}

let context = document.getElementById('container');

context.innerHTML = generateDropdown({
    a: 1,
    b: 2,
    c: 3,
    d: {
        da: 4,
        db: 5,
        dc: 6,
        dd: 7
    },
    e: {
        ea: 8,
        eb: 9,
        ec: {
            eca: 10,
            ecb: 11,
            ecc: 12
        },
        ed: 13
    },
    f: 14
}, context);
.selected {
    background-color: gray;
}

.opened {
    background-color: lightgray;
}

.selected * {
    background-color: white;
}

.multiselect-dropdown li:not(.selected):not(.opened) * {
    display: none;
}
<div id="container" >
</div>

Explanation:

  • generateDropdown generates a recursive ul - li structure (you can change this structure to the one you prefer)
  • select is responsible for selecting and unselecting items
    • selecting a non-composite element just selects it
    • selecting a not selected composite element
      • selects it
      • opens it
    • selecting a selected composite element
      • unselects it
      • selects its closest opened descendant if exists
  • Related