skip to Main Content

I have a form where I am trying to set up a dropdown menu below an input field.

My issue is that I can’t get the dropdown menu to be the same width as the input field above it.

If I set the dropdown menu to use relative positioning and I set it to 75% (the same width as the input field) then it works. But the issue then becomes that the dropdown menu pushes down the elements below it when it expands and that is not what I want. I want it to overlay the elements below it when it expands.

So when I set the dropdown div to use relative positioning the width is ok but then it pushes down the elements which is what I dont want. But then when I set it to use fixed positioning it overlays the elements below it ( which is what I want ) but then I cannot get it to have the same width as the input above it.

How do I make this dropdown div BOTH overlay the elements below it when it extends and ALSO be the same exact width as the input field above it?

Ty for your time and God Bless.

here is a reference to the dropdown div and the input field above it

const input = document.getElementById("bizcategory");
const listContainer = document.getElementById("myList");


const words = ['option5', 'option6', 'option7', 'option8',
  'option9', 'option10', 'option11'
];

for (let i = 0; i < words.length; i++) {
  const newItem = document.createElement("li");
  newItem.textContent = words[i];
  newItem.style.listStyle = "none";
  listContainer.appendChild(newItem);

}



const listItems = listContainer.querySelectorAll("li");

input.addEventListener("focus", () => {
  listContainer.style.display = "block";
})

input.addEventListener("blur", () => {
  setTimeout(() => {
    listContainer.style.display = "none";
  }, 200);
});

listItems.forEach(i => i.addEventListener("click", function() {
  input.value = i.textContent;
  listContainer.style.display = "none";
}));
.signininput {
  height: 40px;
  width: 75%;
  box-sizing: border-box;
}


.list-container {
  display: none;
  border: 1px solid #ccc;
  background-color: white;
  width: 75%;
}

.list-container ul {
  list-style: none;
  padding: 0;
  margin: 0;
  width: 75%;
}

.list-container li {
  padding: 5px;
}

.list-container li:hover {
  background-color: #f0f0f0;
  cursor: pointer;
}
<input class="signininput"  type="text" id="bizcategory" name="bizcategory" placeholder="" maxlength="255" onblur="this.value=removeSpaces(this.value);"></input>
<div class="list-container" id="myList">
  <ul style="list-style: none;">
    <li>Option 1</li>
    <li>Option 2</li>
    <li>Option 3</li>
    <li>Option 4</li>
  </ul>
</div>

4

Answers


  1. First, input elements don’t get a closing tag.

    Next, don’t use inline event handling attributes, like onblur. This is a 25+ year old legacy technique. Instead, separate your HTML and JavaScript by setting up events using the modern .addEventListener().

    Also, there is no need to specify the type attribute on an input if you just want a textbox as that is the default type.

    Now, to your question, why not assign both elements the same CSS class and set the size there?

    document.querySelector("#myList").addEventListener("blur", removeSpaces);
    
    function removeSpaces(e){
     console.log("code goes here")
    }
    .size { 50px; }
    <input class="signininput size" id="bizcategory" name="bizcategory" maxlength="255">
    <div class="list-container size" id="myList" >
       <ul style="list-style: none;">
          <li>Option 1</li>
          <li>Option 2</li>
          <li>Option 3</li>
          <li>Option 4</li>
       </ul>
    </div>

    Is there some reason why a select won’t do what you need here?

    Login or Signup to reply.
  2. Here is some example code i drafted which has the behavior you want. I also recommend using JS events, like .addEventListener() or use mouse attributes like onclick, onmouseover, etc.

    const inputField = document.getElementById('inputField');
    const dropdownMenu = document.getElementById('dropdownMenu');
    
    
    inputField.addEventListener('focus', () => {
        dropdownMenu.classList.add('active');
    });
    
    // hide the dropdown when clicking outside for better user experience :D
    document.addEventListener('click', (event) => {
        if (!inputField.contains(event.target) && !dropdownMenu.contains(event.target)) {
            dropdownMenu.classList.remove('active');
        }
    });
    /* reset and styling for the .form-group */
    .form-group {
        position: relative;
        display: block;
    }
    
    /* styling for the input field */
    .input-field {
        width: 100%; /* makes the input take the full width of its container */
        padding: 8px;
        font-size: 16px;
        box-sizing: border-box;
    }
    
    /* styles for the dropdown menu */
    .dropdown {
        position: absolute;
        top: 100%;
        left: 0;
        width: 100%; /* matches the width of the input field */
        background-color: white;
        border: 1px solid #ccc;
        display: none;
        z-index: 1000;
    }
    
    .dropdown ul {
        list-style-type: none;
        margin: 0;
        padding: 0;
    }
    
    .dropdown li {
        padding: 8px;
        cursor: pointer;
    }
    
    /* and a dropdown to show it works */
    .dropdown.active {
        display: block;
    }
    <div class="form-group">
        <input type="text" id="inputField" class="input-field" placeholder="Enter input" />
        <div class="dropdown" id="dropdownMenu">
            <ul>
                <li>Option 1 :: Change me</li>
                <li>Option 2 :: Change me</li>
                <li>Option 3 :: Change me</li>
                <li>Stackoverflow :D</li>
            </ul>
        </div>
    </div>
    Login or Signup to reply.
  3. Add position: absolute; to .list-container which takes it out of the document flow which makes it "float" over position: static elements (it’s the default for any element. ie. normal flow). As for the widths the following has been add to the <input>:

    font: inherit;
    padding: 5px 10px;
    width: 78%;
    

    <input> by default will have a different font-size so they have to be set explicitly. The padding is added because <input> itself is huge. The width was just eyeballed since form fields have browser default styles that do not coincide with normal CSS.

    const input = document.getElementById("bizcategory");
    const listContainer = document.getElementById("myList");
    
    
    const words = ['option5', 'option6', 'option7', 'option8',
      'option9', 'option10', 'option11'
    ];
    
    for (let i = 0; i < words.length; i++) {
      const newItem = document.createElement("li");
      newItem.textContent = words[i];
      newItem.style.listStyle = "none";
      listContainer.appendChild(newItem);
    
    }
    
    const listItems = listContainer.querySelectorAll("li");
    
    input.addEventListener("focus", () => {
      listContainer.style.display = "block";
    })
    
    input.addEventListener("blur", () => {
      setTimeout(() => {
        listContainer.style.display = "none";
      }, 200);
    });
    
    listItems.forEach(i => i.addEventListener("click", function() {
      input.value = i.textContent;
      listContainer.style.display = "none";
    }));
    .signininput {
      font: inherit;
      height: 40px;
      padding: 5px 10px;
      width: 78%;
      box-sizing: border-box;
    }
    
    
    .list-container {
      position: absolute;
      display: none;
      border: 1px solid #ccc;
      background-color: white;
      width: 75%;
    }
    
    .list-container ul {
      list-style: none;
      padding: 0;
      margin: 0;
      width: 75%;
    }
    
    .list-container li {
      padding: 5px;
    }
    
    .list-container li:hover {
      background-color: #f0f0f0;
      cursor: pointer;
    }
    <input class="signininput"  type="text" id="bizcategory" name="bizcategory" placeholder="" maxlength="255" onblur="this.value=removeSpaces(this.value);">
    <div class="list-container" id="myList">
      <ul style="list-style: none;">
        <li>Option 1</li>
        <li>Option 2</li>
        <li>Option 3</li>
        <li>Option 4</li>
      </ul>
    </div>
    <div>XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
    Login or Signup to reply.
  4. I’ve solved it by changing just the css part and added comments to my css changes explaining what each line does.

    const input = document.getElementById("bizcategory");
    const listContainer = document.getElementById("myList");
    
    
    const words = ['option5', 'option6', 'option7', 'option8',
      'option9', 'option10', 'option11'
    ];
    
    for (let i = 0; i < words.length; i++) {
      const newItem = document.createElement("li");
      newItem.textContent = words[i];
      newItem.style.listStyle = "none";
      listContainer.appendChild(newItem);
    
    }
    
    
    
    const listItems = listContainer.querySelectorAll("li");
    
    input.addEventListener("focus", () => {
      listContainer.style.display = "block";
    })
    
    input.addEventListener("blur", () => {
      setTimeout(() => {
        listContainer.style.display = "none";
      }, 200);
    });
    
    listItems.forEach(i => i.addEventListener("click", function() {
      input.value = i.textContent;
      listContainer.style.display = "none";
    }));
    .signininput {
      height: 40px;
      width: 75%;
      box-sizing: border-box;
    }
    
    
    .list-container {
      display: none;
      border: 1px solid #ccc;
      background-color: white;
      width: 75%;
      
      margin-bottom: -2px; /* fix jitter when selecting input */
      height: 0; /* take up 0 vertical space */
      overflow: visible; /* show dropdown stuff anyway even if it doesn't fit in the 0px height */
    }
    
    .list-container ul {
      list-style: none;
      padding: 0;
      margin: 0;
      
      width: 100%; /* changed from 75% to 100% to get input and dropdown item widths to match */
    }
    
    .list-container li {
      padding: 5px;
      
      box-sizing: border-box; /* fix width mismatch due to padding */
      background-color: white; /* make it non transparent */
      position: relative; /* this is needed for line above to work */
    }
    
    .list-container li:hover {
      background-color: #f0f0f0;
      cursor: pointer;
    }
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width">
        <title></title>
      </head>
      <body>
        <input class="signininput"  type="text" id="bizcategory" name="bizcategory" placeholder="" maxlength="255" onblur="this.value=removeSpaces(this.value);"></input>
      <div class="list-container" id="myList">
        <ul style="list-style: none;">
          <li>Option 1</li>
          <li>Option 2</li>
          <li>Option 3</li>
          <li>Option 4</li>
        </ul>
      </div>
      <div>
        Some random text here that's long enough for testing purposes (want dropdown to cover it but it should not be pushed down).
      </div>
      </body>
    </html>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search