Imitate the actions of a <select>, <option> using a <ul>, <li> structure

I'm currently facing a challenge with my dropdown menu. I am trying to replicate the functionality of a select and option using a ul and li.

When I click on the second li, I want it to replace the first li without removing the latter.

Essentially, I want it to behave like the appendChild() method but without deleting the old element.

const listMenu = Array.from(document.querySelectorAll('ul li'))
const menu = document.querySelector('ul')

const arrow = document.querySelector('ul')

let isDisplay = true

function openMenu() {
    listMenu.slice(1).forEach(list => {
        list.style.display = 'block'
        arrow.classList.add('arrow-up')
        isDisplay = false
    })
}

function closeMenu() {
    listMenu.slice(1).forEach(list => {
        list.style.display = 'none'
        arrow.classList.remove('arrow-up')
        isDisplay = true
    })
}

function displayMenu() {
    if (isDisplay) {
        openMenu()
    } else {
        closeMenu()
    }
}

closeMenu()

menu.addEventListener('click', displayMenu)
ul {
    width: 170px;
    list-style-type: none;
    display: flex;
    flex-direction: column;
    color: #fff;
    font-weight: bold;
    font-size: 1.1em;
    position: relative;
    border: 1px solid #901c1c;
    border-radius: 5px;
    padding: 0;
    cursor: pointer;
}

li {
    background-color: #901c1c;
    padding: 20px 20px;
}

li:not(:first-child) {
    position: relative;
}

li:not(:first-child)::before {
    content: "";
    position: absolute;
    top: 0px;
    left: 5px;
    width: 90%;
    height: 1px;
    background-color: rgb(255, 255, 255);
}

ul::after {
    content: "";
    position: absolute;
    right: 25px;
    top: 22px;
    width: 0.6em;
    height: 0.6em;
    border-left: 0.2rem solid rgb(255, 255, 255);
    border-bottom: 0.2rem solid rgb(255, 255, 255);
    transform: rotate(315deg);
}

.arrow-up::after {
    transform: rotate(135deg);
    top: 28px;
}
<ul>
    <li id="popularity">Popularité</li>
    <li id="date">Date</li>
    <li id="title">Titre</li>
</ul>

Answer №1

When you don't have jQuery at your disposal, there is another way to achieve the same effect. You can toggle a class called .open and define its styling in the CSS.

Here's how it works: every time a list item is clicked, that item will be added to the beginning of the list. Additionally, the class .open will be removed from the parent element, and CSS rules will ensure only the first item of the list is visible:

ul:not(.open) li:not(:first-of-type) {
  display: none;
}

const menu = document.querySelector('.menu');

menu.addEventListener('click', (e)=>{
  
  let listItem = e.target;
  
  if(menu.classList.contains('open'))
    menu.prepend(listItem);
  
  listItem.closest('ul').classList.toggle('open');
  
})
ul {
    width: 170px;
    list-style-type: none;
    display: flex;
    flex-direction: column;
    color: #fff;
    font-weight: bold;
    font-size: 1.1em;
    position: relative;
    border: 1px solid #901c1c;
    border-radius: 5px;
    padding: 0;
    cursor: pointer;
}

li {
    background-color: #901c1c;
    padding: 20px 20px;
}

li:not(:first-child) {
    position: relative;
}

li:not(:first-child)::before {
    content: "";
    position: absolute;
    top: 0px;
    left: 5px;
    width: 90%;
    height: 1px;
    background-color: rgb(255, 255, 255);
}

ul::after {
    content: "";
    position: absolute;
    right: 25px;
    top: 22px;
    width: 0.6em;
    height: 0.6em;
    border-left: 0.2rem solid rgb(255, 255, 255);
    border-bottom: 0.2rem solid rgb(255, 255, 255);
    transform: rotate(315deg);
    transition: transform .3s;
}

ul.open::after {
  transform: rotate(135deg);
}

.arrow-up::after {
    transform: rotate(135deg);
    top: 28px;
}

ul:not(.open) li:not(:first-of-type) {
  display: none;
}
<ul class="menu">
    <li id="popularity">Popularity</li>
    <li id="date">Date</li>
    <li id="title">Title</li>
</ul>

For those who need multiple menus, here's the solution:

const menus = document.querySelectorAll('.menu');

menus.forEach(menu =>{
  
  menu.addEventListener('click', (e)=>{

    let listItem = e.target;

    if(menu.classList.contains('open'))
      menu.prepend(listItem);

    listItem.closest('ul').classList.toggle('open');

  });
  
});
ul {
    width: 170px;
    list-style-type: none;
    display: flex;
    flex-direction: column;
    color: #fff;
    font-weight: bold;
    font-size: 1.1em;
    position: relative;
    border: 1px solid #901c1c;
    border-radius: 5px;
    padding: 0;
    cursor: pointer;
}

li {
    background-color: #901c1c;
    padding: 20px 20px;
}

li:not(:first-child) {
    position: relative;
}

li:not(:first-child)::before {
    content: "";
    position: absolute;
    top: 0px;
    left: 5px;
    width: 90%;
    height: 1px;
    background-color: rgb(255, 255, 255);
}

ul::after {
    content: "";
    position: absolute;
    right: 25px;
    top: 22px;
    width: 0.6em;
    height: 0.6em;
    border-left: 0.2rem solid rgb(255, 255, 255);
    border-bottom: 0.2rem solid rgb(255, 255, 255);
    transform: rotate(315deg);
    transition: transform .3s;
}

ul.open::after {
  transform: rotate(135deg);
}

.arrow-up::after {
    transform: rotate(135deg);
    top: 28px;
}

ul:not(.open) li:not(:first-of-type) {
  display: none;
}
<ul class="menu">
    <li id="popularity">Popularity</li>
    <li id="date">Date</li>
    <li id="title">Title</li>
</ul>

<ul class="menu">
    <li id="popularity2">Popularity</li>
    <li id="date2">Date</li>
    <li id="title2">Title</li>
</ul>

Answer №2

After trying out @prettyInPink's suggested solution, I crafted my own non-jQuery alternative.

You can place this JavaScript snippet anywhere in your file (I believe)--currently, it resides in the header section.

    <script type="text/javascript" >
    
        function ModifyDropdowns(select_xx)
        {
            debug_mode = false;
            
            menu_array = document.getElementsByClassName("state");
            
            if (debug_mode) { menu_string = "<?php echo  'header-games.php:ModifyDropdowns - '.__LINE__; ?>" + ":\n\n" + "select_xx = " + select_xx + "\n" + "menu_array.length = " + menu_array.length + "\n"; }
            
                if (select_xx.slice(0, 6) == "select") { country_str = select_xx.slice(-2); }
                else { country_str = select_xx.slice(0, 2); }

                for (counter=0; counter<menu_array.length; counter++)
                {
                    if (debug_mode) 
                    { 
                        menu_string += "country_str = " + country_str + " - ";
                        menu_string += "menu_array["+ counter + "].id.slice(-2) = " + menu_array[counter].id.slice(-2) + "\n";
                        menu_string += "menu_array[" + counter + "] = " + menu_array[counter].id;
                    } // if (debug_mode)
    
                    if (country_str == menu_array[counter].id.slice(-2)) 
                    { 
                        document.getElementById(menu_array[counter].id).style.display="flex"; 
                        if (debug_mode) { menu_string += " - displaying" + "\n"; } 
                    } // if (("states_" + select_xx) == menu_array[count].id)
                    else
                    { 
                        document.getElementById(menu_array[counter].id).style.display="none"; 
                        if (debug_mode) { menu_string += " - hiding" + "\n"; } 
                    } // if (("states_" + select_xx) == menu_array[counter].id) else
                } // for (counter=0; counter<menu_array.length; counter++)
            
            if (debug_mode) { alert(menu_string); }
                                
        } // function ModifyDropdowns(select_xx)

    </script>

This represents the initial drop-down menu:

<ul id="ul_XX" class="menu country">
    <li id="select_XX" onclick="runTest(this.id);">Please Select Country of Residence</li>
    <li id="us">United States</li>
    <li id="gu">Guam</li>
    <li id="pr">Puerto Rico</li>
    <li id="um">United States Minor Outlying Islands</li>
    <li id="vi">Virgin Islands, U.S.</li>
    <li id="au">Australia</li>
    <li id="ca">Canada</li>
    <li id="va">Holy See (Vatican City State)</li>
    <li id="ie">Ireland</li>
    <li id="im">Isle of Man</li>
    <li id="gb">United Kingdom</li>
    <li id="vg">Virgin Islands, British</li>
</ul>

The <ul> representing states within the "United States" is quite lengthy, so here are just snippets from the ones pertaining to the "United States Minor Outlying Islands" and "Virgin Islands, U.S.":


<ul id="ul_vg" class="menu state twoup">
    <li id="select_vg" onclick="runTest(this.id)">Please Choose Island of Residence</li>
    <li id="vg_AN">Anegada</li>
    <li id="vg_VD">Jost Van Dyke</li>
    <li id="vg_TO">Totola</li>
    <li id="vg_VG">Virgin Gorda</li>
</ul>

<ul id="ul_vi" class="menu state twoup">
    <li id="select_vi" onclick="runTest(this.id)">Please Choose Island of Residence</li>
    <li id="vi_SC">Saint Croix</li>
    <li id="vi_SJ">Saint John</li>
    <li id="vi_ST">Saint Thomas</li>
</ul>

Please ensure that this JavaScript block follows your final second-level menu--for reasons unknown to me. :-(

<script type="text/javascript" >

    function runTest(select_xx)
    {   
        debug_mode = false;
    
        const menuNode = document.getElementById("ul_" + select_xx.slice(-2));

        if (debug_mode)
        {
            node_items = "<?php echo  'function index.php:runTest(select_xx) - '.__LINE__; ?>" + ":\n\n" ;
            node_items += "select_xx = " + select_xx + "\n";
            for (let count = 0; count < menuNode.length; count++) 
            {
                node_items += "node[" + count + "] = " + menuNode[count].id + "\n";
            } // for (let count = 0; count < nodeList.length; count++)
            node_items += "\n" + "ul_" + select_xx.substr(-2);
            alert(node_items);
        } // if (debug_mode)

        menuNode.addEventListener("click", (e)=>
            {
                debug_mode = false;
                
                let listItemEl = e.target;
    
                if (debug_mode) { alert("<?php echo  'function index.php:runTest(select_xx) - '.__LINE__; ?>:\n\n" + "listItemEl.id = " + listItemEl.id); }
          
                if(menuNode.classList.contains("open")) { menuNode.prepend(listItemEl) };
          
                listItemEl.closest("ul").classList.toggle("open");
                
                ModifyDropdowns(listItemEl.id);
            } // "click", (e)=>
        ) // ul_select_xx.addEventListener("click", (e)=>
        
    } // function runTest()

</script>

A substantial amount of debugging was necessary to get this functioning correctly, and I've preserved some of the debugging code in case you find it useful--feel free to remove it from your code once everything works as intended.

To witness this code in action, visit

I trust that this information proves beneficial.

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

Store a designated text into a MySQL database by extracting it from every file within a directory

I've encountered various methods for "crawling" websites, but none seem to fit my requirements. I am interested in being able to extract data from each file within an online directory and then store the extracted information into a MySQL database: h ...

Unable to get autoplay to function for HTML video despite muting the audio and following various browser guidelines

After meticulously following these instructions on incorporating a background video banner into my rails project, I encountered an issue with autoplay. Despite setting up the video to display correctly on my homepage, the autoplay feature doesn't seem ...

What is the reason my function is only operating with a single ID?

I'm attempting to create color-changing "squares" that change color when clicked. The first square changes color correctly, but when I click on the others, the color change only happens on the first square. var image_tracker = 'red'; fu ...

How to line up two blocks side by side in a single block with Bootstrap without the use of CSS

The bootstrap margin is causing issues with this code <div class="row"> <div class="row1 col-lg-3"> <div class="row2 col-lg-1"></div> <div class="row2 col-lg-1"></di ...

Navigate down to the bottom of the element located on the webpage

I'm trying to create a feature where clicking an anchor tag will smoothly scroll to a specific element on the page. Currently, I am using jquery scrollTo for this purpose. Here's the code snippet: $.scrollTo( this.hash, 1500, { easing:&apos ...

Discovering the list of database names and table names in sqliteHere is how you can

I am in the process of developing a SQLite command editor for an Android application using Cordova. Within the app, users will have the ability to create unlimited tables and databases. When they enter the application, they must select a database from a ...

What could be the reason for req.route displaying the previous route instead of

Question: const router = express.Router(); router .route('/:id') .delete( validate(messageValidator.deleteById), MessageController.deleteById, ) .get( validate(messageValidator.getById), MessageController.getById, ); ...

Retrieve the Date information and transfer it to another page using a combination of jQuery, JavaScript, and PHP

I feel defeated trying to solve this problem. Can anyone offer assistance in figuring this out? I've spent an entire day debugging with no success. I have two PHP files, index.php and home.php. In the index.php file, I include the Date range picker, ...

Trouble with Mongoose: Data Not Being Saved

I'm encountering difficulties with a basic query on my database. While following this tutorial at https://scotch.io/tutorials/build-a-restful-api-using-node-and-express-4, the author receives a JSON object containing the name field (the only custom fi ...

The previous user's selection of checkboxes will be disabled

https://i.stack.imgur.com/fH1g2.pnghttps://i.stack.imgur.com/Oiu6q.pngI am facing an issue where I want all the records (seats selected by the user) to be disabled, but the code provided only fetches the last record and disables it. <?php ...

indicating the vertical size of a paragraph

Is there a way to set the specific height for the <p> tag? ...

Does JSON have a special reserved key for identifying the time?

I've noticed an interesting issue when logging the json string of a key labeled tid - it always shows up as 0. Take a look at this example: var transaction = {tid:1, type:0, time:126312736}; var transStr = JSON.stringify(transaction); console.log(tra ...

Tips for customizing the appearance of the day button in a React Material-UI date picker

In my React project, I am using material-ui date picker and I am looking for a way to customize the styling of the day buttons. Specifically, I want to change the text color of the available days. By default, as seen in the screenshot, the text color is bl ...

Is there a better option than using public methods when transitioning from class-based to function-based React components?

When working with React components that utilize hooks, they must be function-based rather than class-based. I've encountered a challenge transitioning from calling methods on child components in class-based components to achieving the same functionali ...

Is it possible to have several responsive images layered on top of one main responsive image

I'm currently working on a map project that involves multiple map pointers (7). By using the code below, I have successfully positioned them correctly: <div style="position:relative; left: 0; top: 0;"> <img src="images/JCCareas.png" cla ...

Adjust the DIV shape to fit perfectly within the browser window without overlapping with any other elements

http://jsbin.com/iGIToRuV/1/edit Currently, I am working on developing a WYSIWYG website designer as part of an experimental project for various purposes. The ultimate goal is to ensure that it is both desktop and mobile-friendly. However, I have encount ...

Fundamental modeling using Node.js with Mongoose

Currently, I am facing a challenge with developing a node.js application. My goal is to create a model for a musical scale that includes a name and a set of associated notes: var mongoose = require('mongoose'); var Schema = mongoose.Schema; var ...

Verify whether the value of the Object matches the value of the string

Currently, I have a situation in ES6 where I am utilizing Vue.js for the current module. The task at hand is to verify if a specific string value exists within an array object. let obj1 = [{name: "abc ced", id: 1}, {name: "holla' name", i ...

I am looking to adjust the height of my MUI Grid component

Recently exploring React, and I'm looking to set a height limit for MUI Grid. I've structured my project into 3 sections using MUI grid components. My goal is to restrict the page height in order to eliminate the browser scrollbar. If the conten ...

problems arise due to padding and floating on the navigation bar

I have been facing a couple of challenges with my code. Firstly, I am trying to remove the small annoying line underneath my selected navigation links and also attempting to float both the header and footer to the right without losing their backgrounds. I ...