Custom Animated Glowing Dropdown Using HTML, CSS, and JavaScript

By Bytewebster - May 14, 2023



Welcome to Bytewebster JavaScript projects on creating a custom animated glowing dropdown using HTML, CSS, and JavaScript. as we all know a dropdown, also known as a select or select box, is an HTML element that allows users to select one option from a list of pre-defined options. So let's know the working style of this project.

Working

Basically it has three dropdown menus and all three have different functionality. The first dropdown is the default one, but it also includes animation effects. Like, when the options appear, glowing colorful lights are displayed in the background. And the second one is much more accessible too and the last one is completely motion-reduced.

                     
                 
Detailed Overview of Project

Mainly most of the CSS were used to create this animated glowing dropdown menu. And we also used a little bit of JavaScript to make it look more functional. It is also worth noting that the code given below is for explanation purposes only, to help you better understand this project.

By adding some animations and glowing effects to a dropdown menu, it can help to improve the overall look and functionality of a website and also you can create a more experience for users.


HTML Structure


First of all, we will see how to prepare the HTML structure of this project then after that we'll move on to CSS and javascript. The code starts with the first three header elements with h3 tags are used to provide some context for the following div elements.

Then comes the first div element with a class of select, which is actually used to create a dropdown menu with a button, When the button is clicked or focused, a list of items appears. Each items represented by an a element with a role attribute of "button".

After that the tabindex attribute is set to "0" for each of these elements to ensure they can be interacted with via keyboard navigation. The second div element with a class of select is similar to the first one, but it has some additional styles applied via a style attribute.


<main>
    <header>
        <h3>Default:</h3>
    </header>
    <header>
        <h3>Accessible:</h3>
    </header>
    <header>
        <h3>Motion-reduced:</h3>
    </header>
    <div class="select" tabindex="0" role="button">
        <button tabindex="0">Select Components</button>

        <div>
            <a role="button" tabindex="0" href="#"><span>Artboards</span></a>
            <a role="button" tabindex="0" href="#"><span>Pages</span></a>
            <a role="button" tabindex="0" href="#"><span>Templates</span></a>
            <a role="button" tabindex="0" href="#"><span>Profile</span></a>
        </div>
    </div>
    <div class="select" tabindex="0" role="button" style="--a11y: 1; --outline-color: hsla(calc(var(--accent-color-hue) * 1deg) 100% 58% / calc(var(--a11y) * 100%));">
        <button tabindex="0">Select Components</button>

        <div>
            <a role="button" tabindex="0" href="#"><span>Artboards</span></a>
            <a role="button" tabindex="0" href="#"><span>Pages</span></a>
            <a role="button" tabindex="0" href="#"><span>Templates</span></a>
            <a role="button" tabindex="0" href="#"><span>Profile</span></a>
        </div>
    </div>

    <div class="select nomotion" tabindex="0" role="button">
        <button tabindex="0">Select Components</button>

        <div>
            <a role="button" tabindex="0" href="#"><span>Artboards</span></a>
            <a role="button" tabindex="0" href="#"><span>Pages</span></a>
            <a role="button" tabindex="0" href="#"><span>Templates</span></a>
            <a role="button" tabindex="0" href="#"><span>Profile</span></a>
        </div>
    </div>
</main>

Next, This div has a custom CSS variable set for --a11y, which likely stands for "accessibility" and that dropdown. This variable is used to control the color of the focus outline that appears around the button and dropdown menu items when they are interacted with via keyboard.

Please note that the style property --outline-color has been set to a CSS hsla color value that is calculated using the calc function and the value of --a11y.

Finally we came to the third and last div element which has a class of nomotion, which likely disables any animations or transitions that would normally occur when the dropdown menu is opened or closed. The functionality is otherwise the same as the first div element.


Styling With CSS


Now, we'll dive into the CSS portion of the code to add some style and animation to our dropdown. So let's get started!

The CSS code of this glowing dropdown starts from the main block of code which defines the properties for the main container element. It sets the maximum width to be automatically determined by the content and centers the container horizontally using the margin property.

Next the @media block of code contains styles that will be applied only when the screen width is greater than or equal to 760 pixels. In this case, the grid-template-columns property is updated to set the number of columns to three.


*, *:before, *:after {
  box-sizing: border-box;
  outline: calc(var(--debug)*1px) dotted red;
  outline-offset: -1px;
}

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

body {
  background: var(--background-color);
  font-family: "Mona sans", sans-serif;
}

main {
  max-width: auto;
  margin: 16em auto;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(1, 1fr);
  grid-gap: 32px;
  place-items: center;
  align-items: start;
}

@media (min-width: 760px) {
  main {
    grid-template-columns: repeat(3, 1fr);
  }
}
header {
  color: white;
}
header h3 {
  letter-spacing: 0.15em;
}

div.select {
  color: white;
  background: var(--dark-color) radial-gradient(ellipse 70% 70% at calc(var(--bg-x)*1%) calc(var(--bg-y)*1%), var(--radial-bg-color) 0%, var(--dark-color) 100%);
  padding: var(--inner-padding);
  border-radius: var(--outer-radius);
  position: relative;
  width: 200px;
  z-index: 1;
  transition: background 0.3s ease, --bg-y 0.4 ease, --bg-x 0.4s ease;
}
div.select:hover {
  animation: glow 1.2s ease-in-out;
}
div.select:hover > button:after {
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='58' height='98' fill='none'%3E%3Cpath fill='hsl(219deg 100% 58%)' d='M25.536 6c1.54-2.667 5.388-2.667 6.928 0l18.187 31.5c1.54 2.667-.385 6-3.465 6H10.814c-3.079 0-5.003-3.333-3.464-6L25.536 6ZM25.536 92c1.54 2.667 5.388 2.667 6.928 0l18.187-31.5c1.54-2.667-.385-6-3.465-6H10.814c-3.079 0-5.003 3.333-3.464 6L25.536 92Z'/%3E%3C/svg%3E") no-repeat center center/0.6em;
}
div.select:before {
  content: "";
  display: block;
  position: absolute;
  width: calc(100% - 2px);
  height: calc(100% - 2px);
  top: 1px;
  left: 1px;
  background: var(--dark-color);
  border-radius: var(--outer-radius);
  z-index: -1;
}
div.select > button {
  padding: calc(var(--inner-padding)*2) calc(var(--inner-padding)*2);
  background: var(--background-color);
  border-radius: var(--inner-radius);
  border: 0;
  color: white;
  text-align: left;
  font-size: 1em;
  width: 100%;
  cursor: pointer;
  position: relative;
  box-shadow: inset 0 2px 1px -1px rgba(255, 255, 255, 0.1);
  transform: scale(var(--scale));
  animation-duration: 0.2s;
  animation-timing-function: cubic-bezier(0.66, -0.82, 0.33, 1.73);
}
div.select > button:focus {
  outline: 1px solid var(--accent-color);
  outline-offset: -1px;
}
div.select > button:after {
  content: "";
  position: absolute;
  right: 8px;
  height: 100%;
  width: 1em;
  top: 0;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='58' height='98' fill='none'%3E%3Cpath fill='hsl(211deg 23% 51%)' d='M25.536 6c1.54-2.667 5.388-2.667 6.928 0l18.187 31.5c1.54 2.667-.385 6-3.465 6H10.814c-3.079 0-5.003-3.333-3.464-6L25.536 6ZM25.536 92c1.54 2.667 5.388 2.667 6.928 0l18.187-31.5c1.54-2.667-.385-6-3.465-6H10.814c-3.079 0-5.003 3.333-3.464 6L25.536 92Z'/%3E%3C/svg%3E") no-repeat center center/0.6em;
}
div.select > div {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: 0;
  transition: height 0.3s ease-in-out;
}
div.select > div > a {
  display: block;
  padding: calc(var(--inner-padding)*1.6) calc(var(--inner-padding)*1.2);
  color: var(--light-color);
  cursor: pointer;
  margin-top: 8px;
  text-decoration: none;
  border-radius: var(--inner-radius);
  position: relative;
}
div.select > div > a > span {
  position: relative;
  display: block;
  transform: translateY(calc(var(--item-y)*1px));
  opacity: var(--item-opacity);
  transition: --item-y 0.2s ease 0.1s, --item-opacity 0.2s 0.1s;
}
div.select > div > a:nth-child(1) span {
  transition-delay: 0.1s;
}
div.select > div > a:nth-child(2) span {
  transition-delay: 0.15s;
}
div.select > div > a:nth-child(3) span {
  transition-delay: 0.2s;
}
div.select > div > a:focus {
  outline: 1px solid var(--outline-color);
  outline-offset: -1px;
}
div.select > div > a:hover, div.select > div > a:focus {
  color: var(--accent-color);
}
div.select:focus-within {
  outline: 1px dashed var(--outline-color);
}
div.select:hover > div, div.select:has(button:focus) > div, div.select:focus-within > div {
  height: calc(var(--max-height)*1px);
  --item-y: 0;
  --item-opacity: 1;
}

.select.nomotion {
  transition: none !important;
  animation: none !important;
}
.select.nomotion:before, .select.nomotion:after,
.select.nomotion *, .select.nomotion *:before, .select.nomotion *:after {
  transition: none !important;
  animation: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .select {
    transition: none !important;
    animation: none !important;
  }
  .select:before, .select:after,
.select *, .select *:before, .select *:after {
    transition: none !important;
    animation: none !important;
  }
}
@keyframes glow {
  from {
    --radial-bg-color: var(--accent-color);
    --bg-x: 100;
    --bg-y: 0;
  }
  50% {
    --radial-bg-color: hsl(290deg 100% 58%);
    --bg-x: 60;
    --bg-y: 120;
  }
  to {
    --radial-bg-color: var(--dark-color);
    --bg-x: 60;
    --bg-y: 120;
  }
}
@keyframes popOut {
  from {
    --scale: 1;
  }
  50% {
    --scale: 1.02;
  }
  to {
    --scale: 1;
  }
}

After all that the header block of code defines the properties for the header element. It sets the color to white. and the div .select block of code defines the properties for a select element. which sets the text color to white and the background color to a gradient.

The final and last part defines the style for a custom select element, which replaces the default select element with a stylized version. It also defines a hover effect and animation for the element.


JavaScript Explanation


Now that we have completed the HTML and CSS part, it's time to dive into the real magic of this custom animated glowing dropdown, which is the Javascript Part.

The JavaScript code begins by selecting all elements on the page with a class of "select" using the document.querySelectorAll() method. Then, an event listener is added to the window object to listen for the "DOMContentLoaded" event, which fires when the page has finished loading. Inside the event listener, a forEach() loop is used to iterate over each "select" element.

For each "select" element, the JavaScript code gets a reference to the button element and creates an empty array called "full_height". It then uses the querySelectorAll() method to select all "a" elements that are direct children of "div" elements inside the "select" element.

Next, an event listener is added to each "a" element that listens for a "click" event. When an "a" element is clicked, the JavaScript code gets the text content of the clicked element and the current text content of the button element.


const selects = document.querySelectorAll('.select');

window.addEventListener('DOMContentLoaded', () => {
  selects.forEach(select => {
    const button = select.querySelector('button');
    const full_height = [];
    
    [...select.querySelectorAll('div > a')].map(link => {
      const styles = window.getComputedStyle(link);
      const box = link.getBoundingClientRect();
      const margin = parseFloat(styles.marginTop) + parseFloat(styles.marginBottom) || 0;
      const height = box.height + margin;
      full_height.push(height);

      link.addEventListener('click', () => {
        const link_text = link.textContent;
        const button_text = button.textContent;
        button.textContent = link_text;
        button.style.animationName="popOut";
        button.addEventListener("animationend", () => {
          button.style.animationName="none"
        });
        const span = document.createElement('span');
        span.textContent = button_text;
        link.innerHTML = "";
        link.appendChild(span)
        link.blur()
      })
    });
    
    const totalHeight = full_height.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
    select.dataset.totalHeight = totalHeight;
    select.style.setProperty('--max-height', totalHeight);  
  });
});

After the animation is triggered, the JavaScript code creates a new span element and sets its text content to the original text content of the button. Finally, it calls the blur() method on the clicked "a" element to remove focus from it.

Finally, the JavaScript code calculates the total height of all the "a" elements inside the "select" element by adding up the heights stored in the "full_height" array using the reduce() method.


We are grateful for your time and attention, and we trust that you have found the project to be interesting.




Video of the Project

Take This Short Survey!


Download Source Code Files

From here You can download the source code files of this JavaScript Animated Glowing Dropdown.
If you are just starting in web development, these snippets will be useful. We would appreciate it if you would share our blog posts with other like-minded people.


Download Source Code
Please wait ...
If the download didn't start automatically, click here

ByteWebster Play and Win Offer.

PLAY A SIMPLE GAME AND WIN PREMIUM WEB DESIGNS WORTH UPTO $100 FOR FREE.

PLAY FOR FREE





Connect With Us

we would like to keep in touch with you..... Register Here.

JOIN US JOIN TELEGRAM