skip to Main Content

I am trying to make a drop down menu in which there are selections consisting of a p tag in which there is a span tag. The problem is that when I select one of the options in the drop down menu it merges the text from the p and span tag together, and they should have different styles but they turn out to be the same.

const dropdowns = document.querySelectorAll('.start__dropdown');

dropdowns.forEach(dropdown => {
  const select = dropdown.querySelector('.select');
  const caret = dropdown.querySelector('.caret');
  const menu = dropdown.querySelector('.menu');
  const options = dropdown.querySelectorAll('.menu li');
  const selected = dropdown.querySelector('.selected');
  select.addEventListener('click', () => {
    select.classList.toggle('select-clicked');
    caret.classList.toggle('caret-rotate');
    menu.classList.toggle('menu-open')
  });
  options.forEach(option => {
    option.addEventListener('click', () => {
      selected.innerHTML = option.innerText;
      select.classList.remove('select-clicked');
      caret.classList.remove('caret-rotate');
      menu.classList.remove('menu-open');
      options.forEach(opt => {
        opt.classList.remove('active');
      });
      option.classList.add('active');
    });
  });
});
.start__dropdown {
  width: 360px;
  position: relative;
  height: 68px;
  border-bottom: 1px solid $text-black;
}

.select {
  background-color: $white;
  display: flex;
  justify-content: space-between;
  align-items: center;
  cursor: pointer;
  padding: 16px;
  height: 100%;
  transition: background 300ms;
}

.selected {
  font-size: 16px;
  line-height: 28px;
  font-weight: bold;
  color: $accent-black;
}

.selected span {
  color: $text-black;
  margin-left: 8px;
}

.caret {
  transition: 300ms;
}

.caret-rotate {
  transform: rotate(180deg);
}

.menu {
  z-index: 1;
  list-style: none;
  padding: 0px 30px;
  background: $white;
  border: 1px solid $text-black;
  border-radius: 8px;
  position: absolute;
  top: 76px;
  left: 50%;
  transform: translateX(-50%);
  width: 367px;
  opacity: 0;
  display: none;
  transition: 200ms;
}

.menu li {
  font-size: 16px;
  line-height: 28px;
  font-weight: bold;
  color: $accent-black;
  cursor: pointer;
  width: 100%;
  height: 68px;
  border-bottom: 1px solid $text-black;
  padding: 20px 16px;
}

.menu li:last-child {
  border: none;
}

.menu li span {
  color: $text-black;
  margin-left: 8px;
}

.menu-open {
  display: block;
  opacity: 1;
}
<div class="start__dropdown">
  <div class="select">
    <p class="selected">Basic Pack <span>Free</span></p>
    <div class="caret"><img src="icons/sign-up/icon-arrow-down.svg" alt="caret"></div>
  </div>
  <ul class="menu">
    <li class="active">Basic Pack <span>Free</span></li>
    <li>Pro Pack <span>$9.99</span></li>
    <li>Ultimate Pack <span>$19.99</span></li>
  </ul>
</div>

I tried using option.innerText to get the text content of the selected option.

I used option.querySelector('span').innerText to get the <span> content from the selected option.

Then I updated the content of the .selected element with .innerHTML, inserting the text of the option and its price into the <span>, but the price was duplicated and it looked like this: <p class="selected">Ultimate Pack $19.99 <span>$9.99</span></p>

options.forEach(option => {
  option.addEventListener('click', () => {
    // Get the text content and the span content of the selected option
    const optionText = option.innerText;
    const optionPrice = option.querySelector('span').innerText;
    // Update the selected item text including the span
    selected.innerHTML = `${optionText} <span>${optionPrice}</span>`;
    // Close the dropdown and remove classes
    select.classList.remove('select-clicked');
    caret.classList.remove('caret-rotate');
    menu.classList.remove('menu-open');
    // Remove 'active' class from all options
    options.forEach(opt => {
      opt.classList.remove('active');
    });
    // Add 'active' class to the selected option
    option.classList.add('active');
  });
});

2

Answers


  1. The issue is because you’re retrieving the innerText of the selected option, so the span is lost. Change this to innerHTML:

    selected.innerHTML = option.innerHTML;
    

    Here’s a full working example, but note that I amended the CSS to specifically change the color of the selected span to red to highlight the effect:

    const dropdowns = document.querySelectorAll('.start__dropdown');
    
    dropdowns.forEach(dropdown => {
      const select = dropdown.querySelector('.select');
      const caret = dropdown.querySelector('.caret');
      const menu = dropdown.querySelector('.menu');
      const options = dropdown.querySelectorAll('.menu li');
      const selected = dropdown.querySelector('.selected');
      
      select.addEventListener('click', () => {
        select.classList.toggle('select-clicked');
        caret.classList.toggle('caret-rotate');
        menu.classList.toggle('menu-open')
      });
      
      options.forEach(option => {
        option.addEventListener('click', () => {
          selected.innerHTML = option.innerHTML;
          select.classList.remove('select-clicked');
          caret.classList.remove('caret-rotate');
          menu.classList.remove('menu-open');
          options.forEach(opt => {
            opt.classList.remove('active');
          });
          option.classList.add('active');
        });
      });
    });
    .start__dropdown {
      width: 360px;
      position: relative;
      height: 68px;
      border-bottom: 1px solid $text-black;
    }
    
    .select {
      background-color: $white;
      display: flex;
      justify-content: space-between;
      align-items: center;
      cursor: pointer;
      padding: 16px;
      height: 100%;
      transition: background 300ms;
    }
    
    .selected {
      font-size: 16px;
      line-height: 28px;
      font-weight: bold;
      color: #000;
    }
    
    .selected span {
      color: #C00;
      margin-left: 8px;
    }
    
    .caret {
      transition: 300ms;
    }
    
    .caret-rotate {
      transform: rotate(180deg);
    }
    
    .menu {
      z-index: 1;
      list-style: none;
      padding: 0px 30px;
      background: $white;
      border: 1px solid $text-black;
      border-radius: 8px;
      position: absolute;
      top: 76px;
      left: 50%;
      transform: translateX(-50%);
      width: 367px;
      opacity: 0;
      display: none;
      transition: 200ms;
    }
    
    .menu li {
      font-size: 16px;
      line-height: 28px;
      font-weight: bold;
      color: $accent-black;
      cursor: pointer;
      width: 100%;
      height: 68px;
      border-bottom: 1px solid $text-black;
      padding: 20px 16px;
    }
    
    .menu li:last-child {
      border: none;
    }
    
    .menu li span {
      color: $text-black;
      margin-left: 8px;
    }
    
    .menu-open {
      display: block;
      opacity: 1;
    }
    <div class="start__dropdown">
      <div class="select">
        <p class="selected">Basic Pack <span>Free</span></p>
        <div class="caret"><img src="icons/sign-up/icon-arrow-down.svg" alt="caret"></div>
      </div>
      <ul class="menu">
        <li class="active">Basic Pack <span>Free</span></li>
        <li>Pro Pack <span>$9.99</span></li>
        <li>Ultimate Pack <span>$19.99</span></li>
      </ul>
    </div>
    Login or Signup to reply.
  2. You have to use innerHTML instead of innerText. Actually innerHTML consider HTML tags as well as their content.

    So, in option section you should insert below code.

    selected.innerHTML = option.innerHTML;
    

    below picture, illustrate different method to get content of a HTML element

    method for accessing the content of HTML element

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search