What is the best way to implement a sliding animation for a toggle button?

Currently, I am working on a toggle bar element that is functioning correctly in terms of styling the toggled button. However, I would like to add more animation to enhance the user experience.

The concept is to have the active button slide to the newly selected button for visual appeal. This means incorporating a sliding animation into the current code.

I am facing difficulty in figuring out how exactly to implement this feature. It seems that simply adding background and styles to the active element will not suffice; a floating or sliding box may be necessary.

While I prefer not to have someone implement it for me, I could use some guidance on where to start. Should I work with pseudo-elements? How can I ensure that the white button background element stretches to the width of the word behind it?

Although this is primarily a CSS-related question, I am unsure of how to initiate the implementation process.

If anyone has any advice, tips, or can point me in the right direction on how to achieve this effect, I would greatly appreciate it! Cheers!

Feel free to check out what I've built so far in CodeSandbox

Answer №1

To achieve a visually appealing effect of a white block moving behind text but in front of backgrounds, you can create a gap by using pseudo elements with lower z index to apply background colors onto elements.

By clicking on a button, you can calculate the distance the white block needs to move from the previously clicked button to reach the current one, as well as adjust its width accordingly.

In this code snippet, the 'white block' is actually represented by a pseudo element attached to the currently clicked button. This allows for easy determination of its final position directly under the active button.

const buttons = document.querySelectorAll('button');

function clicked(e) {
  const el = e.target;
  const prevEl = document.querySelector('button.animate');
  const x = (prevEl == null) ? '0%' : (prevEl.getBoundingClientRect().x - el.getBoundingClientRect().x) + 'px';
  const w2 = window.getComputedStyle(el).width;
  const w1 = (prevEl == null) ? 0 : window.getComputedStyle(prevEl).width;

  buttons.forEach(button => {
    button.classList.remove('animate');
  });
  el.style.setProperty('--x', x);
  el.style.setProperty('--w1', w1);
  el.style.setProperty('--w2', '100%');
  el.classList.add('animate');
}
buttons.forEach(button => {
    button.addEventListener('click', clicked);
  }

);
nav {
  display: inline-block;
  position: relative;
}

nav::before {
  content: '';
  position: absolute;
  background-color: gold;
  width: 100%;
  height: 100%;
  left: 0 top: 0;
  z-index: -3;
}

button {
  margin: 2vmin;
  background-color: transparent;
  position: relative;
}

button::before {
  content: '';
  position: absolute;
  width: 100%;
  height: 100%;
  top: 0;
  left: 0;
  background-color: brown;
  z-index: -2;
}

button.animate::after {
  content: '';
  position: absolute;
  background-color: white;
  width: 100%;
  height: 100%;
  animation-name: move;
  animation-duration: 3s;
  animation-fill-mode: forwards;
  top: 0;
  left: 0;
  z-index: -1;
}

@keyframes move {
  0% {
    transform: translateX(var(--x));
    width: var(--w1);
  }
  100% {
    transform: translateX(0);
    width: var(--w2);
  }
}

@keyframes animate {
  0% {}
  100% {
    background-color: white;
  }
}
<nav>
  <button>AAAA</button>
  <button>AA</button>
  <button>AAAAAA</button>
  <button>A</button>
</nav>

Answer №2

If you're starting from scratch and want the flexibility to adjust the position of a white box in relation to a yellow box (using position: relative or absolute), one approach is to hardcode a function that takes the desired box coordinates. This function would then check if the white box is already in the correct place, and if not, move it x pixels in the right direction. The function could call itself recursively with a timeout, gradually moving the box closer to its target position.

This method essentially involves making small adjustments every few milliseconds until the white box reaches its final destination. By moving the box once every (1000/24)ms or less, the movement should appear smooth and fluid.

When it comes to determining the size of the box, you have the option to either hardcode the dimensions or create a function that calculates the appropriate size based on the text content provided.

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

The Angular Material Table experienced a collapse when trying to render over 20 columns simultaneously

Currently, I am experiencing an issue in Angular Version 5 where the Angular Material Table collapses when rendering more than 20 columns. Here is a snapshot of what my table looks like: https://i.stack.imgur.com/MXfvQ.png https://i.stack.imgur.com/XHWgq ...

A tool that enhances the visibility and readability of web languages such as HTML, PHP, and CSS

Looking to organize my own code examples, I need a way to display my code with syntax highlighting. Similar to how Symfony framework showcases it on their website: http://prntscr.com/bqrmzk. I'm wondering if there is a JavaScript framework that can a ...

difficulties arise when adjusting font size in html and css

When I first created an all HTML/CSS website, I used clickable images as a menu that scaled perfectly on all monitors and browsers. Now, for my new project, I wanted to use a background image for the menu with hyperlinked text on top. I thought setting the ...

V-Calendar is not displaying the accurate dates

https://i.stack.imgur.com/zk4h7.png The image displays dates starting on June 1, 2022, which should be a Wednesday but appears as a Sunday on the calendar. This issue affects all months as they start on a Sunday instead of their respective weekdays. The p ...

Is Vue instance created if element is present?

I am currently developing an application where some pages contain Vue instances, while others do not. My issue arises when switching from one page to another, and the following warning message appears: [Vue warn]: Cannot find element: #element-id How can ...

The alignment of SVG is not in sync with the aspect ratio

I've been struggling with this issue for some time now, trying to align the SVGs correctly for different width and height values. My viewBox has an aspect ratio of 16:9, and I want to scale the SVG accordingly. It's working fine for certain combi ...

What considerations should I keep in mind when changing the DOCTYPE from html 4.01 transitional to 'html'?

Firefox is telling me that my pages are being rendered in 'standards compliance mode' based on the doctype... <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> After changing it to < ...

What is the best way to eliminate the input border?

click here for image I'm currently working on designing a straightforward login page. However, I've noticed that there are black borders around the input fields. Can anyone help with how to remove these borders? ...

How to access vue.js 3 single file component functions from within a script tag

Imagine having a single file component structured like this: <template> // content irrelevant </template> <script> export default { data() { return { redLocations: [ "Isfahaan", "Qom", ...

Attempting to achieve the effect where the entire row of a table changes color when the mouse hovers

I'm attempting to create a gradient effect for an entire row in a table when the mouse hovers over any cell within that row. Despite using what I believe is the correct CSS code, nothing changes upon mouseover (even though the original gradient appear ...

The headline is being improperly rendered inside a black box on Firefox

A while back, I inquired about creating a black box with padding around a two-line headline. The solution worked well, except for older versions of Internet Explorer. However, in Firefox, there are occasional issues with the display looking jagged. This oc ...

I'm facing an issue where the data I retrieved is not displaying properly in my template within nuxt 3

After fetching data from an api, I can see it logged in my async function. However, the data stored in my array is not rendering on my template in Nuxt 3 The script setup includes: //ARRAY OF ALL THE DAILY WEATHER DATA PER DAY let allDataWeather=[]; ( ...

Proper syntax for writing this line in Vue is as follows:

Can you identify the correct syntax for this Vue line: <iframe src="/'product.id'/file.php' " frameborder="0"></iframe> It has been transformed into: <iframe src="/5/file.php"> ...

Tips for utilizing both the Element Selector and Class Selector in Jquery

I am working with a simple table that has TDs assigned either the 'PLUS' or 'MINUS' class. My goal is to use jQuery to implement functionality for collapsing all or expanding all. When the Expand All button is clicked, I need to chang ...

Is it possible to change the file name of vendors.js using webpack?

During the process of compiling a Vue.js project with Webpack, I need to make adjustments to the filenames. I am currently working with webpack version 4.x.x and vuejs version 2.x.x. Specifically, I have the requirement to change the names of "vendors.js ...

Upon returning to the website homepage from another page, the JavaScript animation ceases

I implemented a unique typewriter animation on my homepage by utilizing code from this source and making minor HTML edits to customize the content. https://codepen.io/hi-im-si/pen/DHoup. <h5> <body>Hello! I am Jane.</body> < ...

What is the best way to align a series of divs vertically within columns?

Struggling to find the right words to ask this question has made it difficult for me to locate relevant search results. However, I believe a picture is worth a thousand words so...https://i.stack.imgur.com/LfPMO.png I am looking to create multiple columns ...

Delaying the intensive rendering process in Vue.js and Vuetify: A comprehensive guide

Recently, while working on a project with Vue.js 2 and Vuetify 2.6, I encountered an issue with heavy form rendering within expansion panels. There seems to be a slight delay in opening the panel section upon clicking the header, which is most likely due t ...

SVG padding will only take effect if two or more sides are specified

What is the reason behind an SVG requiring at least 2 areas to be padded for padding to apply? For instance, applying padding-top: 50px to an svg element will have no effect. However, adding padding-top: 50px; padding-left: 1px will result in the padding ...

React state update not triggering a component re-render

I've been attempting to toggle the visibility of a div by clicking on another div, but I'm encountering an issue. The div becomes invisible on the first click only if it was visible initially. After that, it remains invisible and does not update. ...