Creating a sliding bottom border effect with CSS when clicked

Is there a way to animate the sliding of the bottom border of a menu item when clicked on?

Check out the code below:

HTML -

<div class="menu">
    <div class="menu-item active">menu 1</div>
    <div class="menu-item">menu 2</div>
    <div class="menu-item">menu 3</div>
  </div>

LESS -

 .menu-item {
      float: left;
      border-bottom: 2px solid grey;
      margin: 0 10px;
    padding: 0 10px;
      cursor: pointer;

  &.active {
    border-color: blue;
  }
}

JS -

$(document).ready(function(){
    $(".menu-item").on("click", function(e) {
      $(".menu-item").removeClass("active");
        console.log("yo");
        $(this).addClass("active");
      });
});

View the working demo here.

If you want to see how the bottom border can slide from one menu item to another upon click, check out this demo here.

Please note that the jQuery solution in the second demo is what I am trying to achieve with pure CSS.

Answer №1

After conducting thorough research, it turns out that achieving this effect purely with CSS (especially with dynamic elements and width) is challenging. As a solution, I have developed a jQuery plugin that combines both jQuery and CSS to accomplish the desired outcome. Below is a snippet of my jQuery plugin:

jQuery:

$.fn.RegisterTabBar = function (id) {
    var $ele = $(this);
    if ($ele.prop("tagName") && $ele.prop("tagName").toLowerCase() == "ul" && id !== undefined) {
        $ele.attr("data-style-id", id);
        $("<style type='text/css'></style>").attr("id", id).appendTo($("body"));
        $ele.find("li").on("click", function () {
            var $li = $(this),
                CurrentIndex = $ele.find(".active").removeClass("active").index(),
                ClickedIndex = $li.addClass("active").index();

            function SetStyle($ele, $li, IsToLeft) {
                var ID = $ele.attr("data-style-id");
                if (ID === undefined) return;
                if ($ele.width() <= 0) {
                    setTimeout(function () { SetStyle($ele, $li, IsToLeft); }, 100);
                    return;
                }

                $("style#" + ID).text(
                    "ul[data-style-id='" + ID + "']:before { " +
                        "left: " + $li.position().left + "px; " +
                        "right: " + ($ele.width() - $li.position().left - $li.outerWidth()) + "px; " +
                        "-webkit-transition: left " + (IsToLeft ? ".45s" : ".8s") + ", right " + (IsToLeft ? ".9s" : ".3s") + "; " +
                        "transition: left " + (IsToLeft ? ".45s" : ".8s") + ", right " + (IsToLeft ? ".9s" : ".3s") + "; " +
                    "} "
                );
            }
            SetStyle($ele, $li, ClickedIndex < CurrentIndex);
        });
    }
    return $ele;
}

CSS:

ul.tab-bar,
ul.tab-bar > li { position: relative; }
    ul.tab-bar.bar-style-1:before { background-color: #00668f; } /* Color of the bar*/
    ul.tab-bar:before {
        display: inline-block;
        content: '';
        position: absolute;
        bottom: 0; left: 0;
        height: 3px;
        z-index: 1;
    }
    ul.tab-bar > li { list-style-type: none; border-width: 0; }
    ul.tab-bar.bar-style-1 > li.active { color: #00668f; font-weight: 700; }
    li {
            list-style-type: none;
            display: inline-block;
            border-width: 0;
            height: 40px;
            margin: 0 20px;
            background-color: transparent;
            color: #9c9c9c;
            text-align: center;
            cursor: pointer;
        }

You can try out the implementation on https://jsfiddle.net/czf9kjfd/

Edit 1 --

For those looking for a 'pure' CSS version that works with a fixed number of items and utilizes jQuery to toggle the active class, here is the CSS implementation:

CSS:

ul li {
  display: inline-block;
    font-weight: bold;
    width: 30%;
    padding: 7px 0;
    text-align: center;
    cursor: pointer;
  list-style-type: none;
}
ul li:last-child {
  margin: 0;
  padding: 0;
    height: 7px;
    background: #00b6ff;
  float: left;
}

ul li.active:nth-child(1) ~ li:last-child,
ul li:nth-child(1):hover ~ li:last-child {
    margin-left: 0%;
}

ul li.active:nth-child(2) ~ li:last-child,
ul li:nth-child(2):hover ~ li:last-child {
    margin-left: 30%;
}

ul li.active:nth-child(3) ~ li:last-child,
ul li:nth-child(3):hover ~ li:last-child {
    margin-left: 60%;
}

li:last-child {
    -webkit-transition: margin-left 0.2s ease;
    -moz-transition: margin-left 0.2s ease;
    -o-transition: margin-left 0.2s ease;
    transition: margin-left 0.2s ease;
    position: relative;
}

You can experiment with this version on: https://jsfiddle.net/yqpnckw4/1/

Answer №2

To achieve the desired effect, it is not possible to animate borders directly. Instead, consider creating a separate element that mimics a bottom border for each element and then animate either its width or position.

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 toggle class feature of jQuery is malfunctioning when placed inside a different div

Hello everyone, I am currently working on a toggle effect on my webpage. However, I encountered an error when trying to move the button close to another part of the page. The button works fine if it is placed in one part of the HTML, but does not work if i ...

Is it possible to detect inline elements that have been wrapped?

I am facing a challenge with displaying an indefinite amount of in-line elements. Depending on the width of the browser, some elements may shift to a new line. I am curious to know if it is possible to identify and isolate these rows of elements, or if the ...

No content in Django's Bootstrap dropdown menu

I need assistance with extracting a list of objects and placing them in a dropdown menu. I have successfully achieved this, but the issue seems to be related to my HTML structure. Here is the code snippet from my HTML: <div class="dropdown"> < ...

Safari is experiencing issues with overflow-x functionality after a device rotation

I'm currently in the process of developing a website that requires a specific div element to be horizontally scrollable if its content exceeds the screen size. This functionality works smoothly on most browsers, except for Safari: Scenario: When the ...

Discovering and sorting an array in Vue.js based on IDs

Hello everyone, I've been attempting to filter my results using the filter and includes methods but it doesn't seem to be working. Does anyone have a solution for this, perhaps involving includes or something similar? companies ids [1,2,3] user c ...

What is the best way to create a shape using CSS?

In my chat application, I have a functionality where replies on a chat are initially hidden and represented by a count in a red box. When the user clicks on the red button, the replies from other users are revealed along with an input box for writing a r ...

Clear information from a trio of tables utilizing ajax

I have successfully implemented a project in Yii and now I am facing an issue with deleting data from a table using ajax function. In the controller, I have written the following code: public function actionDelete1(){ if (isset($_POST['x1'] ...

Unsupported MIME format

I'm encountering an issue where my stylesheet is not applying to my handlebars. It was working fine yesterday, but today I'm seeing a MIME error and I'm unsure of the reason behind it. Any assistance would be greatly appreciated. Server Cod ...

What is causing the error message of "prop id does not match the rendered server output" to appear on my screen?

https://i.stack.imgur.com/VOLDT.png I've been working on a nextjs redux project and I keep running into this error whenever I refresh my page. Despite searching for a solution, I haven't been able to resolve it. The official next js blog suggest ...

Utilize the Sed command to manipulate the input stream by substituting all occurrences of HTML <i> tags with <em> tags

I am attempting to create a regex with the sed command in order to process the input stream and replace all HTML tags with em tags. For example: This is <i id="x">emphasized text</i> and <i>so is this</i>. should be replaced by Thi ...

Parsing error: 'Unexpected token' found in JSON while attempting to access an external file

Currently working on implementing backbone for a new project along with underscore, requirejs, jquery, and bootstrap. Things are progressing smoothly as I aim to include static survey question data into one of the data models. { "defaultOptions": { ...

Having trouble finding a solution because Ajax/JSON is only retrieving a single result from the PHP/SQL file

I am facing an issue with retrieving data through AJAX/JSON from another file containing a PHP While loop. The problem is that only one data is being returned instead of the expected 11 rows. Despite researching extensively on Google, YouTube, and other so ...

Combining Multiple Arrays into One | Node.js

Can someone explain how to merge an array of arrays to me? For instance, I have the following array: [ [ {brand: 'fiat', model: 'palio'} ], [ {brand: 'nissan', model: 'march'} ] ] I want to transform this array int ...

Implementing Ajax Requests in PHPUnit tests for Laravel applications

Working on Laravel tests using phpunit, I ran a test that involves inputting data into a textbox and clicking a button. This triggers an Ajax post request to the server, which then returns a response. If the response is a string like the one in the code sn ...

Tips for extracting text from a textarea while retaining newline characters:

When using jquery, my goal is to retrieve the text from a textarea element with new lines represented as \n instead of br tags. However, I encountered an issue where Firefox debugger does not display the \n or br characters when I select it and r ...

How to Link Laravel 5 with Ajax?

I've implemented this Laravel code in my controller for the detach function. $input = Input::all(); $product= Products::findOrFail($input['product_id']); $product->tags()->detach($input['tag_id']); $product= Prod ...

Retrieve a collection of nested dictionaries containing flask and angular data

In my app development journey with flask and ionic(angular), I'm working on returning a JSON list. Here's the python code snippet: def get-stocks(): # Select all stocks cursor.execute("""SELECT * FROM `tbl_symbol_index`"" ...

Implementing a smooth camera movement in Three.js using the mousewheel

Is there anyone who can assist me with achieving smooth camera movement (forward/backward) using the mouse wheel? The current code I have is not providing the desired smoothness. document.addEventListener( 'mousewheel', onDocumentMouseWheel, fal ...

Encountering a Vue syntax error following the binding of a session variable

Encountering a syntax error while attempting to bind a session variable as a prop of my Vue component. Scrutinizing my code did not reveal any mistakes, but perhaps another set of eyes may catch something. This is where I have registered my components: V ...

Using jQuery to trigger a Bootstrap modal when a button is clicked

I've been attempting a simple task, but it appears to be unsuccessful. I'm trying to handle the click event of a button inside a bootstrap modal, and so far, nothing happens when clicking the button. My guess is that my jQuery selector for select ...