Dynamic Bootstrap Tab menu slider with movable functionality when the limit is reached

I am facing an issue with the tabs menu on my website. The tabs menu items can vary between 2 or 3 to even 20 or more. When the number of navigation items exceeds 10, they automatically drop to the second line, which you can see here.

However, I don't want this effect. I want an arrow icon to appear when the navigation limit is reached, and by clicking on it, the remaining menu items should move to the left.

I have tried the following code but have not been successful:

HTML

<div class="wrap">
  <ul class="nav nav-tabs">
      <li class="active"><a href="#">Home</a></li>
      <li><a href="#">Tab1 example</a></li>
      <li><a href="#">Tab2 example</a></li>
      <li><a href="#">Tab3 example</a></li>
      <li><a href="#">Tab4 example</a></li>
      <li><a href="#">Tab5 example</a></li>
      <li><a href="#">Tab6 example</a></li>
      <li><a href="#">Tab7 example</a></li>
      <li><a href="#">Tab8 example</a></li>
      <li><a href="#">Tab9 example</a></li>
      <li><a href="#">Tab10 example</a></li>
      <li><a href="#">Tab11 example</a></li>
      <li><a href="#">Tab12 example</a></li>
  </ul>
</div>
<button id="goPrev">Prev</button>
<button id="goNext">Next</button>

CSS

.wrap {
  overflow: hidden;
  position: relative;
  white-space: nowrap;
  width:1000px;
  background: #dad9d9;
    border: 1px solid #efefef;
}

.wrap>.nav-tabs {
  display: inline-block;
  padding:0;
  margin:0;
  position: relative;
  top: 0;
  left: 0;
}

.wrap>.nav-tabs>li {
  background: #fff;
  display: inline-block;
  position: relative;
  white-space: normal;
  float: none;
}
.nav-tabs>li>a {
  margin-right: 0;
}

JavaScript/JQuery

var wrap = $(".wrap").width();
var el = $("ul").width();
if (el > wrap) {
  $("#goNext").click(function() {
    $("ul").stop(true);
    if (-parseInt($("ul").css("left")) - wrap < 0) {
      $("ul").animate({
        "left": "+=-" + wrap
      }, "slow")
    } else {
      $("ul").animate({
        "left": "-" + (el - wrap)
      }, "slow")
    }

  });
  $("#goPrev").click(function() {
    $("ul").stop(true);
    if (-parseInt($("ul").css("left")) - wrap > 0) {
      $("ul").animate({
        "left": "+=" + wrap
      }, "slow")
    } else {
      $("ul").animate({
        "left": "0"
      }, "slow")
    }

  });
}

Bootply

Note: I do not want to rely on any external plugins for this small customization. Also, please ensure that the slider remains responsive.

Thank you

Answer №1

One way to handle page navigation is by creating a global variable called "currPage." Here is an example implementation (View on jsfiddle):

var menus = $("#menus"), menuWidth = menus.parent().outerWidth();
    var menupage = Math.ceil(menus[0].scrollWidth / menuWidth), currPage = 1;
    if (menupage > 1) {
        $("#goNext").click(function () {
            currPage < menupage && menus.stop(true).animate({
                "left": -menuWidth * currPage
            }, "slow") && currPage++
        });
        $("#goPrev").click(function () {
            currPage > 1 && menus.stop(true).animate({
                "left": -menuWidth * (currPage - 2)
            }, "slow") && currPage--;
        });
        $(window).on("resize", function () {
            menuWidth = menus.parent().outerWidth();
            menupage = Math.ceil(menus[0].scrollWidth / menuWidth);
            currPage = Math.ceil(-parseInt(menus.css("left")) / menuWidth) + 1;
        });
    }
/* Optional theme */
@import url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css');

.tab-slider {
  padding:0 40px;
}
.tab-slider .btn-icon {
    position: absolute;
    top: 5px;
}
#goPrev {
  left:0;
}
#goNext {
  right:0;
}
.wrap {
  overflow: hidden;
  position: relative;
  white-space: nowrap;
  width: 100%;
  background: #dad9d9;
  border: 1px solid #efefef;
  font-size: 0;
}

.nav-tabs>li.active>a,
.nav-tabs>li.active>a:focus,
.nav-tabs>li.active>a:hover {
  border: 1px solid transparent;
}

.wrap>.nav-tabs {
  display: inline-block;
  padding: 0;
  margin: 0;
  position: relative;
  top: 0;
  left: 0;
}

.wrap>.nav-tabs>li {
  background: #fff;
  display: inline-block;
  position: relative;
  white-space: normal;
  float: none;
  font-size: 14px;
}

.nav-tabs>li>a {
  margin-right: 0;
  border-radius: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<div class="tab-slider">
  <div class="wrap">
    <ul class="nav nav-tabs" id="menus">
      <li class="active"><a href="#">Home</a></li>
      <li><a href="#">Tab1 example</a></li>
      <li><a href="#">Tab2 example</a></li>
      <li><a href="#">Tab3 example</a></li>
      <li><a href="#">Tab4 example</a></li>
      <li><a href="#">Tab5 example</a></li>
      <li><a href="#">Tab6 example</a></li>
      <li><a href="#">Tab7 example</a></li>
      <li><a href="#">Tab8 example</a></li>
      <li><a href="#">Tab9 example</a></li>
      <li><a href="#">Tab10 example</a></li>
      <li><a href="#">Tab11 example</a></li>
      <li><a href="#">Tab13 example</a></li>
      <li><a href="#">Tab14 example</a></li>
      <li><a href="#">Tab15 example</a></li>
      <li><a href="#">Tab16 example</a></li>
      <li><a href="#">Tab17 example</a></li>
    </ul>

  </div>
  <button id="goPrev" class="btn btn-default btn-icon"><i class="glyphicon glyphicon-chevron-left"></i></button>
  <button id="goNext" class="btn btn-default btn-icon"><i class="glyphicon glyphicon-chevron-right"></i></button>
</div>

Note: An ID menus has been added to the <ul class="nav nav-tabs">

Answer №2

I implemented a fix width solution by calculating the size of each item, and it is now functioning perfectly.

function itemWidth() {
  // Determine the largest item to set the width for all items
  var largestWidth = 0;
  $(".nav-tabs>li>a").each(function() {
    if ($(this).outerWidth() > largestWidth) {
      largestWidth = $(this).outerWidth();
    }
  });
  Wrap = $(".wrap").outerWidth(); // the total width of the wrapper
  someSpace = largestWidth + 10; // create some space, 5px on each side
  roundFigure = Wrap / someSpace; // divide the parent width by the width of the largest item
  roundFigure = Math.round(roundFigure); // round the figure 
  finalWidth = Wrap / roundFigure; // get the final width 
  $(".nav-tabs>li>a").outerWidth(finalWidth); // set the final width
}

itemWidth(); // call the function

// Perform the animation
var menus = $("#menus"),
  menuWidth = menus.parent().outerWidth();
var menupage = Math.ceil(menus[0].scrollWidth / menuWidth),
  currPage = 1;
if (menupage > 1) {
  $("#goNext").click(function() {
    currPage < menupage && menus.stop(true).animate({
      "left": -menuWidth * currPage
    }, "slow") && currPage++
  });
  $("#goPrev").click(function() {
    currPage > 1 && menus.stop(true).animate({
      "left": -menuWidth * (currPage - 2)
    }, "slow") && currPage--;
  });
  // Refresh on resize 
  $(window).on("resize", function() {
  itemWidth();
    menuWidth = menus.parent().outerWidth();
    menupage = Math.ceil(menus[0].scrollWidth / menuWidth);
    currPage = Math.ceil(-parseInt(menus.css("left")) / menuWidth) + 1;
  });
}
@import url('//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css');
.tab-slider {
  padding: 0 40px;
}

.tab-slider .btn-icon {
  position: absolute;
  top: 5px;
}

#goPrev {
  left: 0;
}

#goNext {
  right: 0;
}

.wrap {
  overflow: hidden;
  position: relative;
  white-space: nowrap;
  width: 100%;
  background: #dad9d9;
  border: 1px solid #efefef;
  font-size: 0;
}

.nav-tabs>li>a {
  text-align: center;
  padding-left: 0;
  padding-right: 0;
}

.nav-tabs>li.active>a,
.nav-tabs>li.active>a:focus,
.nav-tabs>li.active>a:hover {
  border: 1px solid transparent;
}

.wrap>.nav-tabs {
  display: inline-block;
  padding: 0;
  margin: 0;
  position: relative;
  top: 0;
  left: 0;
}

.wrap>.nav-tabs>li {
  background: #fff;
  display: inline-block;
  position: relative;
  white-space: normal;
  float: none;
  font-size: 14px;
}

.nav-tabs>li>a {
  margin-right: 0;
  border-radius: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="tab-slider">
  <div class="wrap">
    <ul class="nav nav-tabs" id="menus">
      <li class="active"><a href="#">Home</a></li>
      <li><a href="#">Tab1 example</a></li>
      <li><a href="#">Tab2 example</a></li>
      <li><a href="#">Tab3 example</a></li>
      <li><a href="#">Tab4 example</a></li>
      <li><a href="#">Tab5 example</a></li>
      <li><a href="#">Tab6 example</a></li>
      <li><a href="#">Tab7 example</a></li>
      <li><a href="#">Tab8 example</a></li>
      <li><a href="#">Tab9 example</a></li>
      <li><a href="#">Tab10 example</a></li>
      <li><a href="#">Tab11 example</a></li>
      <li><a href="#">Tab13 example</a></li>
      <li><a href="#">Tab14 example</a></li>
      <li><a href="#">Tab15 example</a></li>
      <li><a href="#">Tab16 example</a></li>
      <li><a href="#">Tab17 example</a></li>
    </ul>

  </div>
  <button id="goPrev" class="btn btn-default btn-icon"><i class="glyphicon glyphicon-chevron-left"></i></button>
  <button id="goNext" class="btn btn-default btn-icon"><i class="glyphicon glyphicon-chevron-right"></i></button>
</div>

Answer №3

When the UL element <ul class="nav nav-tabs"> is given a very large width, the tabs will be displayed in a single long line.

To handle this situation, you might need to utilize JavaScript to assess the window width and the total width of the tabs in order to determine whether the arrow navigation is necessary.

Subsequently, the arrow buttons can be used to trigger JavaScript functions that will move the UL to reveal the hidden tabs.

Update: For a practical demonstration, refer to this example fiddle: https://jsfiddle.net/hq0189rd/

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

Pressing the button on the form has no effect

I am testing a login screen using NodeJS, but I am facing an issue where the submit button does not redirect to the dashboard screen as intended. What could be missing in the code? The submit button should direct to the dashboard screen that I have created ...

Exploring Vue.Js and Airtable: Mastering the Art of Filtering

I am currently working on a project using vue.js to showcase information from an Airtable database. I am looking to implement a filtering functionality for the data displayed. The table contains education details, including subject information like biology ...

Observes the behavior of a React component with the context.router property being undefined

Currently, I am conducting a test on a react component using chai, karma, and enzyme. mount( <Provider store={configureStore(mockContext, null)}> <Component {...props} /> </Provider> ) In the component, I am utilizing context.router.l ...

Using JQUERY to toggle classes conditionally

$('.showall').css("cursor","pointer").click(function() { $('.value-container').toggle(); $('.dim-header').toggleClass($('.dim-header').toggleClass() == 'dim-header' ? 'dim-header active' : & ...

Learn how to pass an id from the query parameters to the getInitialProps function in Next.js

Looking to create a basic website that displays content from a database? Utilizing the getInitialProps function from Next.js documentation can help with server-side rendering: https://nextjs.org/docs/api-reference/data-fetching/getInitialProps#getinitialpr ...

What is the best way to rephrase a sentence that contains a double negative

Figuring out how to negate a condition often requires hours of trial and error to avoid breaking anything. Is there any method for negating conditions other than relying on intuition? For instance: //x and y are whole numbers !((x<9) && (y&l ...

The Kendu UI Tabsbstrip views are experiencing issues with loading jquery validation

While using Kendo UI's Tabstrip, I encountered an issue. Specifically, I have two tabs within the tabstrip and I would like to implement client-side jQuery validation in one of the views under the tabstrip. Here is what it looks like on Google Chrome ...

Using vanilla JavaScript with AJAX, the second asynchronous post will only be sent once the first post has been successfully sent

I am in the process of creating a HotSpot that offers internet access once users input their email addresses. To make this function properly, I need to execute two separate AJAX posts: The first one sends hidden username and password details to the rout ...

Having trouble with the ionic ion-nav-buttons not working on initial load?

My table view triggers the PixCtrl controller when a cell is clicked. If the connection is successful, data is retrieved using $http.get, otherwise sqlite is used. This setup works fine. However, I am facing an issue with my ion-nav-buttons being dynamic. ...

Error [ERR_MODULE_NOT_FOUND]: Module could not be located in vscode

Issue with VS Code: Module Not Found Error View the image associated with the erroreN.png ...

How can I bring in a dynamic MaterialUI theme object from another file?

Could anyone provide guidance on the correct syntax for importing a dynamic theme object from a separate file? I am attempting to retrieve the system theme based on a media query and then dynamically set the theme. Everything works as expected when all t ...

Is it possible to use jQuery to set a value for a form control within an Angular component?

I'm currently working on an Angular 5 UI project. In one of my component templates, I have a text area where I'm attempting to set a value from the component.ts file using jQuery. However, for some reason, it's not working. Any suggestions o ...

By employing jQuery ajax and leaving out a value for the form action attribute

Recently, I designed an ajax sign-up form using jQuery that connects to a PHP backend. One question I have is whether it's wise to keep the form attribute action="" empty and let the ajax interface handle everything. Are there any potential downsides ...

A guide to adjusting the size of an iframe within a bootstrap modal

I am currently facing an issue where I am trying to display an iframe inside a bootstrap modal. The challenge is that the content within the iframe is dynamic, so the size may vary each time the modal is opened. As seen here, the modal appears too big and ...

Steps for populating a datatable from a JSON object with keys and values as columns

Typically, when populating a JQuery datatable, we provide a Json Array containing json objects and map each field to a column. However, I have a single json object and I want to display each key in one column and its corresponding value in the next column ...

A custom JavaScript function designed to replicate Excel's functionality of dividing numbers by thousands

I've noticed a unique behavior in Excel where when a cell is in focus and you enter, for example, 1500.32, it displays as 1 500.32. However, once you click enter or move away from the cell, it changes to 1 500.32. I'm intrigued by how this works. ...

I've been struggling with my Create React app for the past two days, and it just won

When trying to create a React project using the command `npx create-react-app reactproject`, I encountered an error: npm ERR! code ENOENT npm ERR! syscall spawn C:\Users\SUJITKUMAR\Desktop npm ERR! path D:\WebDev\React npm ERR! ...

A comprehensive guide on creating a package that combines an href link with an <li> element

I am currently using the <li> tag to display href links. The issue I am facing is that when I click on the 'box,' it does not directly link to another page. Instead, I have to click on the href link for it to redirect to another page. Is th ...

Error: Null value does not have a property 'tagName' to read

I am having trouble dynamically removing a CSS file from the head tag. I keep receiving this error message: Error: Unable to access property 'tagName' of null Here is my code: Link to CodeSandbox https://i.sstatic.net/Jk3Gt.png export default f ...

How to link a CSS file from a JavaScript file

I have a form for users with fields for first name, last name, password, and confirm password. I've implemented validation to check if the password and confirm password fields match using JavaScript: $(document).ready(function() { $("#addUser").cl ...