Search code examples
javascriptcascadingdropdown

Plain JS Cascading selects of unknown depth


Wanted to make a generic cascading dropdown but am weak in recursion

The code is supposed to end up with

  • One select for items - clothes or gadgets - when a choice is made
    • One select with either Levis/Gucci or LG/Apple - when a choice is made
      • One select with either Levis jeans or jackets or Gucci shoes or dresses - when a choice is made
        • One select with Levis jeans sizes OR levis jacket sizes OR
        • One select with Gucci shoe sizes OR Gucci dress sizes

OR

      • One select with either LG TVs or phones or Apple Macbooks or iPhones - when a choice is made
        • One select with LG TV sizes OR LG Phone sizes OR
        • One select with Apple Macbook sizes OR Apple iPhone sizes

I lost my train of thoughts when I got to actually recurse - or perhaps filtering can be used?

I assume one could make a set of paths and then just show/hide depending on path

const selObject = {
  "-- Select Item --": {
    "Clothes": {
      "-- Select brands --": {
        "Levis": {
          "-- Select product --": {
            "Jeans": {
              "-- Select size --": [
                "38",
                "39",
                "40"
              ]
            },
            "Jackets": {
              "-- Select size --": [
                "41",
                "42",
                "43"
              ]
            }
          }
        }, // end Levis
        "Gucci": {
          "-- Select product --": {
            "Shoes": {
              "-- Select size --": [
                "45",
                "50",
                "55"
              ]
            },
            "Dresses": {
              "-- Select size --": [
                "8",
                "9",
                "10"
              ]
            }
          }
        } // end Gucci
      } // end brands  
    }, // End clothes
    "Gadgets": {
      "-- Select brands --": {
        "LG": {
          "-- Select product --": {
            "TVs": {
              "-- Select size --": [
                "38",
                "39",
                "40"
              ]
            },
            "Phones": {
              "-- Select size --": [
                "8",
                "9",
                "10"
              ]
            }
          }
        }, // end Levis
        "Apple": {
          "-- Select product --": {
            "Macbooks": {
              "-- Select size --": [
                "15",
                "17",
                "21"
              ]
            },
            "iPhones": {
              "-- Select size --": [
                "8",
                "9",
                "10"
              ]
            }
          }
        } // end Apple
      } // end brands
    } // end  Gadgets
  } // end items
} // end  

function createSel(obj) {
  Object.keys(obj).forEach(function(item) {
    if (typeof obj[item] == "object") {
      var list = obj[item];
      //console.log(item,typeof list);
      if (typeof list == "object") {
        if (list.length) {
          list.forEach(function(val) {
            console.log('<br/>'+val)
          })  
        }  
        else createSel(list)
      }
    } else {
      console.log("no", obj[item])
    }
  });
}
window.onload = function() {
  createSel(selObject)
}
<form name="myform" id="myForm">
  <div id="selContainer">
  </div>
</form>


Solution

  • Doing this in React would be easier,. But for a plain JS solution the below might be what your after.

    Basically all I'm doing is using recursion to create the components, and attach the events.

    const selObject = {
      "-- Select Item --": {
        "Clothes": {
          "-- Select brands --": {
            "Levis": {
              "-- Select product --": {
                "Jeans": {
                  "-- Select size --": [
                    "38",
                    "39",
                    "40"
                  ]
                },
                "Jackets": {
                  "-- Select size --": [
                    "41",
                    "42",
                    "43"
                  ]
                }
              }
            }, // end Levis
            "Gucci": {
              "-- Select product --": {
                "Shoes": {
                  "-- Select size --": [
                    "45",
                    "50",
                    "55"
                  ]
                },
                "Dresses": {
                  "-- Select size --": [
                    "8",
                    "9",
                    "10"
                  ]
                }
              }
            } // end Gucci
          } // end brands  
        }, // End clothes
        "Gadgets": {
          "-- Select brands --": {
            "LG": {
              "-- Select product --": {
                "TVs": {
                  "-- Select size --": [
                    "38",
                    "39",
                    "40"
                  ]
                },
                "Phones": {
                  "-- Select size --": [
                    "8",
                    "9",
                    "10"
                  ]
                }
              }
            }, // end Levis
            "Apple": {
              "-- Select product --": {
                "Macbooks": {
                  "-- Select size --": [
                    "15",
                    "17",
                    "21"
                  ]
                },
                "iPhones": {
                  "-- Select size --": [
                    "8",
                    "9",
                    "10"
                  ]
                }
              }
            } // end Apple
          } // end brands
        } // end  Gadgets
      } // end items
    } // end  
    
    
    function fillDropdown(target, obj) {
      const sel = document.createElement("select");
      const sub = document.createElement("div");
      if (typeof obj !== "object") {
        sub.innerHTML = "<p>Thank you for your selection</p>";
        target.appendChild(sub);
        return;
      }
      target.appendChild(sel);
      target.appendChild(sub);
      const [title, value] = Object.entries(obj)[0];
      //add our title option
      const option1 = document.createElement("option");
      option1.innerText = title;
      sel.appendChild(option1);
      //now add the sub items
      const items = Object.entries(value);
      items.forEach(([k, v]) => {
        const option = document.createElement('option');
        option.innerText = k;
        sel.appendChild(option);
      });
      sel.addEventListener("change", () => {
        sub.innerHTML = "";
        if (sel.selectedIndex > 0) {
          const i = items[sel.selectedIndex - 1];    
          fillDropdown(sub, i[1]);
        }
      }); 
    }
    
    
    window.onload = function() {
      //createSel(selObject);
      fillDropdown(
        document.querySelector('#selContainer'),
        selObject
      );
    }
    select {
      display: block;
      width: 100%;
      padding: 10px;
    }
    <form name="myform" id="myForm">
      <div id="selContainer">
      </div>
    </form>