HTML two-row horizontal list with truncation

My dilemma involves a horizontal list of items that expand the full width of their container and wrap onto the next line. However, I only want to show two lines of items initially, with a "show more" link to reveal additional rows in increments of two until all are displayed.

I have provided the following markup, but I'm unsure how to limit the display to just the first two rows. Any suggestions on achieving this? Keep in mind that the width of the containing element can change dynamically, so the two displayed rows should adapt accordingly.

.items {
  overflow-y:hidden;
  max-height:30px;
}

.item {
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  cursor: pointer;
  display: inline-block;
  padding: 0;
  text-align: center;
  text-decoration: none!important;
  vertical-align: middle;
  margin-bottom: 10px;
  margin-right: 10px;
}

.item-inner {
  display: block;
  position: relative;
  overflow: hidden;
  height: 29px;
  border-radius: 2px;
}

.item-text {
  background-color: transparent;
  border: 0;
  display: block;
  font-size: 13px;
  line-height: 29px;
  margin: 0;
  outline: 0;
  padding: 0 10px 0 11px;
  text-align: center;
  white-space: nowrap;
}
<span class="items">
    <span class="item"><span class="item-inner"><span class="item-text">Item 1</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 2</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 3</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 4</span></span></span>
    ...
</span>

Answer №1

Here is a straightforward method to accomplish this.

Begin by enclosing all the items within a div and then add another div above the items for "show more".

<div class="container">
    <div class="next" onclick="changeHeight()">
        <span class="nextText">Show more<span>
    </div>
    <span class="item"><span class="item-inner"><span class="item-text">Item 1</span></span></span>
.........
</div>

Next, include the JS script for the onclick event.

<script>
        function changeHeight(){
            const cont = document.querySelector('.container');
            const divInnerContentHeight = cont.scrollHeight;
            const divClientHeight = cont.clientHeight;
            
            console.log(divInnerContentHeight);
            console.log(divClientHeight);
            
            if(divClientHeight < divInnerContentHeight){
                cont.setAttribute("style",`height:${divClientHeight + 80}px`);
                if(divClientHeight+ 80 >= divInnerContentHeight)
                    document.querySelector('.nextText').innerHTML = "Show less";
            }
            else{
                cont.setAttribute("style",'height:91px');
                document.querySelector('.nextText').innerHTML = "Show more";
            }
        }
    </script>

And here is the end result:

function changeHeight(){
            const cont = document.querySelector('.container');
            const divInnerContentHeight = cont.scrollHeight;
            const divClientHeight = cont.clientHeight;
            
            //console.log(divInnerContentHeight);
            //console.log(divClientHeight);
            
            if(divClientHeight < divInnerContentHeight){
                cont.setAttribute("style",`height:${divClientHeight + 80}px`);
                if(divClientHeight+ 80 >= divInnerContentHeight)
                    document.querySelector('.nextText').innerHTML = "Show less";
            }
            else{
                cont.setAttribute("style",'height:91px');
                document.querySelector('.nextText').innerHTML = "Show more";
            }
        }
.container{
    width: 50%;
    margin: auto;
    height: 91px;
    overflow: hidden;
    padding-bottom: 18px;
}
.next {
    position: relative;
    top: 100%;
    z-index: 100;
    background-color: #d9cece;
    text-align: center;
}
.next:hover {
    background-color: whitesmoke;
    cursor: pointer;
}
    .item {
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  cursor: pointer;
  display: inline-block;
  padding: 0;
  text-align: center;
  text-decoration: none!important;
  vertical-align: middle;
  margin-bottom: 10px;
  margin-right: 10px;
}

.item-inner {
  display: block;
  position: relative;
  overflow: hidden;
  height: 29px;
  border-radius: 2px;
}

.item-text {
  background-color: transparent;
  border: 0;
  display: block;
  font-size: 13px;
  line-height: 29px;
  margin: 0;
  outline: 0;
  padding: 0 10px 0 11px;
  text-align: center;
  white-space: nowrap;
}
<div class="container">
    <div class="next" onclick="changeHeight()">
        <span class="nextText">Show more<span>
    </div>
    <span class="item"><span class="item-inner"><span class="item-text">Item 1</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 2</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 3</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 4</span></span></span>
    <span class="item"><span class="item-inner"><span class="item-text">Item 5</span></span></span>
    ...
</div>

Answer №2

When determining the grouping of elements based on their position, my answer begins by iterating through each item and assigning them to rows based on their top value.

The functionality of the read more button involves adjusting the maximum height of the container element according to the product of element height and row number. Additionally, the read more button is hidden once all rows are displayed.

function implementReadMore() {
  const items = document.querySelector(".items");
  const allItems = items.querySelectorAll(".items .item");
  const readMore = document.querySelector(".readMore");

  let rows = 0;
  let curTop = 0;
  const maxHeight = 40;
  let row = 1;

  items.style["max-height"] = maxHeight + "px";

  allItems.forEach((el) => {
    let rect = el.getBoundingClientRect();
    el.dataset.height = rect.height + "px";
    if (curTop < el.offsetTop) {
      curTop = el.offsetTop;
      rows++;
    }

    el.dataset.row = rows;
  });

  readMore.addEventListener("click", () => {
    if (row < rows) {
      row++;
      items.style["max-height"] = (maxHeight * row) + "px";

      if (row == rows) {
        readMore.classList.add("hide")
      }
    }
  });
}

window.addEventListener('resize', implementReadMore)
implementReadMore();
.items {
  overflow: hidden;
  transition: max-height 0.2s ease-in-out;
}

.item {
  border-radius: 3px;
  border-style: solid;
  border-width: 1px;
  cursor: pointer;
  padding: 0;
  text-align: center;
  text-decoration: none!important;
  vertical-align: middle;
  margin-bottom: 10px;
  margin-right: 10px;
  display: inline-block;
  
}

.hide {
  display: none;
}

.item-inner {
  display: block;
  position: relative;
  overflow: hidden;
  height: 29px;
  border-radius: 2px;
}

.item-text {
  background-color: transparent;
  border: 0;
  display: block;
  font-size: 13px;
  line-height: 29px;
  margin: 0;
  outline: 0;
  padding: 0 10px 0 11px;
  text-align: center;
  white-space: nowrap;
}
<div class="items">
  <span class="item"><span class="item-inner"><span class="item-text">Item 1</span></span>
  </span>
  <span class="item"><span class="item-inner"><span class="item-text">Item 2</span></span>
  </span>
  ...
  </span>
  <span class="item"><span class="item-inner"><span class="item-text">Item 32</span></span>
  </span>
</div>
<button class="readMore">Read More</button>

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

What is the proper way to parse an array of JSON objects?

Here is the data I need help with: var information = [ { "_id": "5e458e2ccf9b1326f11b5079", "xxTitle": "testtttttttttttt", "hhhhhhhhhh": "sssssssss", "xxzzzzzz": null, "oooooooooooooo": "ssssss", "xxDescription": "sssssss", "xxDetails": "ssssssss", "llll. ...

Ways to showcase an alert or popup when clicking?

I am utilizing a date picker component from this site and have enabled the 'disablePast' prop to gray out past dates preventing selection. Is there a way to trigger an alert or popup when attempting to click on disabled days (past dates)? Any sug ...

I am looking to upload an image to the database using ajax or any alternative method

I need assistance in uploading an image to a Spring Boot backend via AJAX or any other method. Below is the img tag and form I have implemented, along with an AJAX request to send form data. How can I include the image in this process? AJAX request (exclu ...

The ng-repeat function is currently disabled and not displaying any data from the JSON object

I am currently facing an issue where the ng-repeat Directive in my code is getting commented out and not displaying the results of the JSON object. I have verified that the object is being properly passed to "this.paises2" using the toSource() method, and ...

Preventing a scroll handler from executing once an element has been clicked

When a user scrolls to the video, it will automatically start playing. Similarly, when the user scrolls away from the video, it will stop playing and display the poster image. However, I encountered an issue where I don't want this functionality to tr ...

As soon as I inserted the image, the text vanished into thin air

The phrase "welcome" is not displaying after I included the av tag <!DOCTYPE html> <html> <style> @font-face { font-family: OpenSans; src: url(OpenSans-Bold.ttf); } * { ...

Is it possible to distinguish and identify each separate window when a user opens multiple instances of your site?

Has anyone explored the possibility of distinguishing between multiple windows a user may open on the same site? My goal is to assign the initial window the name "window1" and subsequent windows "window2" and so forth. Is there a method for achieving this ...

Increasing the nth-child Selector in Jquery

Referring to this I am looking to cycle through the nth-child selector, which involves: var i = 1; var tmp = $(this).find('td:nth-child(i+1)'); // I wonder how this can be achieved i++; I have rows with dynamically generated columns i ...

AngularJS Issue: The function 'FirstCtrl' is missing and is undefined

Currently, I am enrolled in the AngularJS course on egghead.io and have encountered an issue that seems to be a common problem for others as well. Despite trying various solutions found here, none seem to work for me. In the second lesson video, the instru ...

Reveal the MongoDB database connection to different sections within a Next.js 6 application

Currently developing an application using Next.js v6, and aiming to populate pages with information from a local mongodb database. My goal is to achieve something similar to the example provided in the tutorial, but with a twist - instead of utilizing an ...

Increased impact of dynamically added elements following page transition

After implementing dynamic functionality from jQuery in my code, I encountered an issue where if I navigate back one page and then return to the original page containing the added elements, they seem to trigger twice upon clicking. To troubleshoot, I incl ...

Trouble with Leafletjs Routing Machine collapse button loading issue

In my project, I am using django 1.10.5, booststrap 4.0, and LeafletJs 1.0.3 along with the routing machine plugin and geocoder. However, I have encountered an issue where the collapse button of the control panel for the routing machine does not appear (it ...

When a parent element uses the flex property and its child elements have no content, the flex child will expand

I am experiencing a peculiar issue that has left me puzzled. p { padding: 0; margin: 0; } .fc { display: flex; flex-direction: column; } .fc > article { flex: 1 1 auto; display: flex; flex-direction: column; border: 1px solid #000; ...

Adding more rows to the array and then inserting them into the database

Seeking some clarity and appreciation in advance for any assistance! I've organized an array of information within a table that I can edit and then sync with my database once updated. <input type='button' value='add row' id=&a ...

Tips for distinguishing individual rows within a table that includes rowspans?

My Vue application calculates a table with rowspans by using an algorithm based on a configuration file. This allows the application to render columns (and maintain their order) dynamically, depending on the calculated result. For example, see the code sn ...

Convert the easeInExpo function from jQuery easing to vanilla JavaScript and CSS

Currently, I am in the process of converting a piece of code from jQuery to plain JavaScript and CSS. The specific code snippet I am focusing on involves creating easing functions without relying on jQuery. const customEasing = { easeInExpo: function ( ...

What's the best way to include a div on the right side of my adaptable divs?

I need the "div 1" label to always appear on the right side of the divs, while still maintaining responsiveness. Is there a way to achieve this effectively? Struggling to find a wrapper that meets my responsiveness requirements. #wrapper { width: aut ...

Importing Angular libraries with the * symbol

One of the key modules in my library is sha256. To import it, I had to use the following syntax: import sha256 from 'sha256'; However, while researching this issue, I came across a question on Stack Overflow titled: Errors when using MomentJS ...

A full-width CSS menu featuring dropdowns beneath each entry for easy navigation

Check out the JSFiddle here I've created a full-width CSS menu that spans the entire screen, but I'm struggling to figure out how to make the corresponding subnav items appear below their parent items. Is it even possible to achieve this design ...

What causes the indexOf method to return -1 even when the values are present in the array?

Could someone explain why the indexOf() method returns -1 even though the values are present in the array? The includes() function also returns false for me. I feel like I must be missing something or forgetting a crucial detail. Any insights on why the ...