Creating a responsive navigation menu by converting overflowing menu items into a dropdown list

I'm currently designing a menu that contains multiple list items. I would like the overflowing li's to collapse and display in a dropdown when resizing the browser to smaller screens, such as laptops and tablets.

Original Menu.

Responsive view of the menu.

Below is the code structure.

var menuWidth = $('ul').width(); //retrieve ul actual width

var listWidth = $.map($('ul li'), function(val) {
  return $(val).width();
}); //store individual li widths in an array
var arrayTotalValue = listWidth.reduce(function(a, b) {
  return a + b;
}); //add values in the above array
var totalWidth = Math.ceil(arrayTotalValue); //round up total value

if (totalWidth > menuWidth) {
  $('li').css({
    color: '#FF0000'
  })
}
ul {
  display: flex;
  padding: 50px 0;
  box-sizing: border-box;
  align-items: center;
  width: 700px;
  overflow: visible;
}

li {
  text-decoration: none;
  color: #000000;
  font-size: 20px;
  text-transform: capitalize;
  border: 1px solid black;
  margin: 20px 10px;
  list-style: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="menu-item">menu1</li>
  <li class="menu-item">menu2</li>
  <li class="menu-item">menu3</li>
  <li class="menu-item">menu4</li>
  <li class="menu-item">menu5</li>
  <li class="menu-item">menu6</li>
  <li class="menu-item">menu7</li>
  <li class="menu-item">menu8</li>
  <li class="menu-item">menu9</li>
  <li class="menu-item">menu10</li>
  <li class="menu-item">menu11</li>
  <li class="menu-item">menu12</li>
  <li class="menu-item">menu13</li>
  <li class="menu-item">menu14</li>
  <li class="menu-item">menu15</li>
  <li class="menu-item">menu16</li>
</ul>

Here is the link to my codepen. Feel free to provide any feedback or suggestions.

Answer №1

To start with, my approach would involve modifying the CSS for improved readability and functionality. I plan to make adjustments by disabling the flexbox to enable wrapping and ensuring that only elements in the first row are visible through setting a restricted height (max-height) along with overflow:hidden. Additionally, I will introduce a clickable element that appears when there are hidden items. This will be achieved using a pseudo-element method to conceal it.

ul {
  padding:5px 40px 5px 5px;
  box-sizing: border-box;
  overflow: hidden;
  max-height:55px;
  position:relative;
}

li {
  display:inline-block;
  text-decoration: none;
  color: #000000;
  font-size: 20px;
  text-transform: capitalize;
  border: 1px solid black;
  margin:10px;
  list-style: none;
  position:relative;
}

li:last-child::after {
  content:"";
  position:absolute;
  left:102%;
  width:100vw;
  top:-10px;
  bottom:-10px;
  background:#fff;
  z-index:2;
}

.click {
  background:red;
  position:relative;
  width:40px;
  height:40px;
  margin-left:auto;
  margin-top:-65px;
  margin-right:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="menu-item">menu1</li>
  <li class="menu-item">menu2</li>
  <li class="menu-item">menu3</li>
  <li class="menu-item">menu4</li>
  <li class="menu-item">menu5</li>
  <li class="menu-item">menu6</li>
  <li class="menu-item">menu7</li>
  <li class="menu-item">menu8</li>
  <li class="menu-item">menu9</li>
  <li class="menu-item">menu10</li>
  <li class="menu-item">menu11</li>
  <li class="menu-item">menu12</li>
  <li class="menu-item">menu13</li>
  <li class="menu-item">menu14</li>
</ul>
<div class="click"></div>

Subsequently, incorporating some JavaScript code is essential to reveal the concealed elements upon clicking the red square icon, creating a dropdown-like effect. The trick here is to identify hidden items by comparing their offsetTop value against a predefined threshold.

var h = 30;
var val = 0;

$('.click').click(function() {
  if ($('ul').hasClass('show')) {
    $('ul').removeClass('show');
    $('ul li.menu-item').removeClass('drop')
    return;
  }
  val = 0;
  $('ul li.menu-item').each(function() {
    var of = $(this).offset().top - $('ul').offset().top;
    if ( of > 20) {
      $(this).addClass('drop');
      $(this).css('top', 'calc(100% + ' + val + 'px)');
      val += h;
    }
    $('ul').addClass('show');
  })
})
ul {
  padding: 5px 40px 5px 5px;
  box-sizing: border-box;
  overflow: hidden;
  max-height: 55px;
  position: relative;
}

ul.show {
  overflow: visible;
}

li {
  display: inline-block;
  text-decoration: none;
  color: #000000;
  font-size: 20px;
  text-transform: capitalize;
  border: 1px solid black;
  margin: 10px 8px;
  list-style: none;
  position: relative;
}

li.drop {
  display: block;
  position: absolute;
  right: 0;
}

li:last-child::after {
  content: "";
  position: absolute;
  left: 102%;
  width: 100vw;
  top: -10px;
  bottom: -10px;
  background: #fff;
  z-index: 2;
}

li.drop::after {
  content: none;
}

.click {
  background: red;
  position: relative;
  width: 40px;
  height: 40px;
  margin-left: auto;
  margin-top: -65px;
  margin-right: 5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
  <li class="menu-item">menu1</li>
  <li class="menu-item">menu2</li>
  <li class="menu-item">menu3</li>
  <li class="menu-item">menu4</li>
  <li class="menu-item">menu5</li>
  <li class="menu-item">menu6</li>
  <li class="menu-item">menu7</li>
  <li class="menu-item">menu8</li>
  <li class="menu-item">menu9</li>
  <li class="menu-item">menu10</li>
  <li class="menu-item">menu11</li>
  <li class="menu-item">menu12</li>
  <li class="menu-item">menu13</li>
  <li class="menu-item">menu14</li>
</ul>
<div class="click"></div>

The provided example offers a simplified demonstration with specific hardcoded values that are customizable or can be made dynamic as needed.

Answer №2

I have made an attempt to address the issues you are facing. I have come up with a solution to resolve your problem. Below is the method I suggest for fixing the issue.

$('.header').click(function() {
  console.log('open menu');
  if ($(this).parent().hasClass('expanded') == false) {
    $(this).parent().addClass('expanded');
  } else {
    $(this).parent().removeClass('expanded');
  }
})
ul {
  display: flex;
  padding: 50px 0;
  box-sizing: border-box;
  align-items: center;
  width: 700px;
  overflow: visible;
}

ul .header {
  display: none;
}

li {
  margin: 20px 10px;
}

li {
  text-decoration: none;
  color: #000000;
  font-size: 20px;
  text-transform: capitalize;
}

li.cat {
  display: block;
  border: 1px solid black;
}

@media screen and (max-width: 800px) {
  ul {
    display: table-row;
    list-style-type: none;
    margin: 0;
    padding: 0;
  }
  ul .header {
    display: block;
  }
  ul .cat {
    display: none;
  }
}

.expanded li {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<ul>
  <li class="header">dropdown
    <li>
      <li class="cat">menu1</li>
      <li class="cat">menu2</li>
      <li class="cat">menu3</li>
      <li class="cat">menu4</li>
      <li class="cat">menu5</li>
      <li class="cat">menu6</li>
      <li class="cat">menu7</li>
      <li class="cat">menu8</li>
      <li class="cat">menu9</li>
      <li class="cat">menu10</li>
      <li class="cat">menu11</li>
      <li class="cat">menu12</li>
      <li class="cat">menu13</li>
      <li class="cat">menu14</li>
      <li class="cat">menu15</li>
      <li class="cat">menu16</li>
</ul>

The above example demonstrates a simple approach. You can customize it according to your needs.

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

Struggling to link variables and functions to an angularJS controller

When using the $scope object to bind functions and variables, and making changes accordingly in HTML, the code works fine. But when using a different object, it doesn't work as expected. Here is an example of a working code snippet: ...

The custom font fails to load on a customized Material UI theme

In my React web application, I tried to implement a custom font by writing the following code: import React, { FunctionComponent } from 'react' import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles' import SofiaPro ...

Sending an AJAX associative array to a PHP associative array

I'm currently attempting to transmit form values to PHP utilizing the $.ajax method. Here is the HTML form structure I am working with: <form> <p> <label for="login">User ID:</label> <input type="text" name="login" id ...

What is the best way to implement loading a script after an AJAX request has

var currentTallest = 0, currentRowStart = 0, rowDivs = new Array(), $el, topPosition = 0; $('.blocks').each(function() { $el = $(this); topPosition = $el.position().top; if (currentRowStart != topPosition) { // we just came to a new row. ...

Tips for incorporating a mail button to share html content within an Angular framework

We are in the process of developing a unique Angular application and have integrated the share-buttons component for users to easily share their referral codes. However, we have encountered an issue with the email button not being able to send HTML content ...

Creating a design with two divs split by a diagonal line using CSS

Is there a way to make two divs span the full width of the page while being divided in half by a diagonal line using CSS? I am working on a slider and once completed, I need to add content to both parts. Any ideas on how to accomplish this with two divs? ...

What is the best way to handle an AJAX POST request and a resource controller in Laravel version 5.6?

As a newcomer to web development, I am currently learning about JavaScript and AJAX. Through various iterations of the AJAX call, I have managed to grasp the basics of how it works. My current goal is to send the content of my form to the database. Here ...

Vuetify vueJS dialog with elevated design

Is there a way to completely remove the elevation of a v-dialog so that it has no shadow at all? The elevation property in the v-dialog itself and using the class as Class = "elevation-0" have not worked for me. Here is the link to the component ...

What is the best way to navigate through an XML document within the DOM of an HTML

I am facing an issue with HTML code. My goal is to navigate through an XML document directly from within the HTML code. Here is the XML code: <?xml version = "1.0"?> <planner> <year value = "2000"> <date month = "7" day = " ...

Resizing an image within a div with automatic adjustment, centered and aligned to the bottom position

I'm really struggling to understand this concept and not making much progress. I have a background image that automatically scales to fit the window size. In front of it, I want to center an image fixed to the bottom of the page, while still being sca ...

What is the most effective way to assign multiple functions to the onClick event of a button, based on a specific property,

I have a special button that generates a specific type of code when rendered: <button>{this.props.text}</button> This button is named ButtonCustom. In the render method of the main container, I am trying to achieve the following: function my ...

Creating a header for an HTML table with merged columns in jQuery DataTables

Whenever I attempt to implement the jQuery DataTables plugin, it encounters an error stating c is undefined on line 256 (as seen in the minified version). The structure of my table is as follows: <table class="datatable"> <thead> ...

Unusual conduct exhibited by jQuery's css() function for setting the width

My goal is to retrieve the width of the initial .imagen element and apply it to the img within .imagen. HTML: <div class="imagen"> <div class="normal"> <img> </div> <div class="hover"> <img> ...

What is the best way to add a centered progress spinner on top of an overlay?

Here is a code snippet that includes functionality to append a site overlay as needed. I am currently trying to implement the feature of also displaying an image progress spinner in the center of the screen over the overlay. I would prefer if this spinner ...

Adjusting the transparency of numerous elements using JavaScript or jQuery

My task involves creating a large form where elements initially have an opacity of 0.5, and when a button is clicked, the opacity changes to 1.0. While I can achieve this using JavaScript, I want to find a more efficient way by avoiding individual if state ...

How to attach input to function invocation in Angular 2

Can we connect the @Input() property of a child component to a parent component's function call like this: <navigation [hasNextCategory]="hasNextCategory()" [hasPreviousCategory]="hasPreviousCategory()" (nextClicked)="next ...

Issue with the positioning of the datepicker is not functioning properly

I'm currently facing an issue with the position of a date picker plugin from jquery. The search box on my website allows users to select a range of dates using the date picker, but when enabled, the date picker appears at the bottom left corner of the ...

If the condition is based on the category in category.php and single.php

I am attempting to create a conditional statement that will display specific content only if the user is in a particular category or on a single.php page associated with that category. I have experimented with these codes, but unfortunately, neither of the ...

What is the best way to merge different Vue JS instances (each linked to different html divs) into one cohesive unit using Vue

Essentially, I have a scenario where I've created two HTML divs named vueapp1 and vueapp2. Both of these divs serve the same purpose of displaying information and are linked to their individual Vue instances for extracting JSON data and presenting it. ...

Error message "undefined" appears when using jQuery Ajax response in Internet Explorer

Having issues with jquery ajax requests. <script type="text/javascript> $(document).ready(function() { $.ajax({ type: "POST", async: false, cache: false, url: "/ajax/script.php", data: { display: 'u ...