How to break array from filling div if the div is full

Viewed 134

I'd like to ask a question regarding arrays and HTML divs.

If I have an array of:

const exampleArray = ['Germany','Australia','Canada','Mongolia','Argentina','China'];

And let's say I have a div like so, with a max length of around 100px (sorry for the long looking table, I couldn't format it on stack overflow)

| A div |
| -------- | 

I want to try fill up the div as much as possible, but if it overflows, I want it to stop and end with ', ... 5' the number being the number of array items it couldn't print

For example:

| Germany, Australia, Canada, ... 2|
| -------- | 

Not:

| Germany, Australia, Canada, Mong... 2|
| --------- | 

Could anyone advise what is the best way to implement this?

4 Answers

Assuming you want the ellipsis and the bracketed number to be within the 100px, this snippet keeps adding an array item, plus its comma, calculates what the width of the ellipsis and bracketed remaining count is and if it fits, carries on.

If it has got too big then it reverts to the previous content and displays that.

Note: 100px is quite narrow given on my example at least the ellipsis plus bracketed number take at least 30px, so this snippet will show just one country name. Try it with 200px or smaller font size to test for more.

const exampleArray = ['Germany','Australia','Canada','Mongolia','Argentina','China'];
const div = document.querySelector('#list');
const endbit = document.querySelector('#endbit');
let i, endbitLength;
let divPrev = '';
let endbitPrev = '';

for (i = 0; i< exampleArray.length; i++) {
  endbitPrev = endbit.innerHTML;
  endbit.innerHTML = '...(' + (exampleArray.length - i) +')';
  endbitLength = endbit.offsetWidth;
  div.innerHTML = divPrev + exampleArray[i] + ', ';
  if (div.offsetWidth > (100 - endbitLength)) {
    break;
  }
  else {
    divPrev = divPrev + exampleArray[i] + ', ';
    endbitPrev = '...' + '(' + (exampleArray.length - i) + ')';
  }
}
endbit.style.display = 'none';
div.innerHTML = divPrev + endbitPrev;
div {
  display: inline-block;
}
<div id="list"></div>
<div id="endbit">...</div>

You can achieve your desired result by using below example, it is just reference for your requirement, just change the code or logic according.

const exampleArray = ['Germany','Australia','Canada','Mongolia','Argentina','China'];
var width=$(".tobefilled").width();
var news=exampleArray.slice(0,-width/15);
var remaining=exampleArray.length-news.length;
$(".tobefilled").html(news+",..."+remaining);
.tobefilled{
        width:50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="tobefilled"></div>

If you are ok with adding a span inside the div then this would do. I know there are better ways to do it, but you can try this function for now

function addToDiv(arr) {

  const div = document.querySelector("#div");
  const span = div.firstElementChild;
  const divWidth = div.clientWidth;
  let finalIndex = 0;
  let remaining = 0;
  for (const [index,c] of arr.entries())
  { 
      span.append(c)
      if(span.offsetWidth >= divWidth){
           finalIndex = index-1;
           remaining = arr.length - index;
           break;
       }
      if(index != arr.length -1)
            span.append(', ')
    }
  
  if(!remaining) return;
  span.textContent = '';
  for (const [index,c] of arr.entries())
  {
        if(index > finalIndex) break;
        span.append(c+', ')
  }
  span.append('...'+remaining)
    
}

It periodically checks if the width of the span exceeds that of the div. I have written a fiddle here

https://jsfiddle.net/10fbkmg5/4/

You can play with the fiddle by changing the width of the div to check if it fits your requirements.

You can create a clone to measure how many items fit, here is an example:

const exampleArray = ['Germany', 'Australia', 'Canada', 'Mongolia', 'Argentina', 'China'];
const target = document.querySelector('#target');
const temp = target.cloneNode();
temp.style.whiteSpace = 'nowrap';
temp.style.visibility = 'hidden';
target.parentElement.append(temp);

const content = exampleArray.filter((v, i, arr) => {
  temp.textContent = arr.slice(0, i + 1).join(', ');
  if (temp.scrollWidth <= target.clientWidth) {
    // Make sure that this is the last element or if it is not, that the ellipsis still fits.
    if (i === (arr.length - 1)) {
      return true;
    } else {
      temp.textContent += ` ... ${arr.length - (i + 1)}`;
      if (temp.scrollWidth <= target.clientWidth) {
        return true;
      }
    }
  }
});

const restCount = exampleArray.length - content.length;
target.textContent = content.join(', ')
if (restCount) {
  target.textContent += ` ... ${restCount}`;
}
#target {
  width: 200px;
}
<div id="parent">
  <div id="target"></div>
</div>

What this does is:

  • Clone the #target element.
  • Add the clone to the parent of this element (to maximise the changes that it will have the same styling).
  • Filter the strings array. The filter function adds all the strings up to the current iteration's index to the clone and checks if the clone's width is bigger than the original element, it also makes sure that all the items up to this point as well as the ellipsis text will fit.
  • The result of the filter operation is then used to set the original element's text and to calculate the count of the remaining element (the ones that don't fit if any).
Related