简体   繁体   中英

Dynamically flexible width & number items with flex

I have a set of tags, that I want to show in the client. However, sometimes you might have too many tags and you want to show only one row of tags maximized to your body's width without setting a fixed number of columns or item width, and adding a show more button at the end of the tag list with the same style as a tag.

I have achieved this using Javascript in my Angular project by doing the following:

  1. Find out the width of your tags container dynamically, with ViewChild on my content container:

let contentWidth = this.contentContainer.nativeElement.clientWidth;

  1. Calculate the text width of the see more button and use it to calculate the new content width minus see more button width:

Calculating text function does the following:

  const canvas = document.createElement('canvas'); // create a canvas
  const context = canvas.getContext('2d'); // get the context
  context.font = '12px avertastd-bold'; // set up your font and size

And calculate the text width:

const seeMoreButtonWidth = context.measureText(seeMoreButtonText).width;

  1. Create a new array variable 'previewTags' which will hold the tags that are visible when the tags body is in collapsed state, and fill in as many tags as you can by calculating each tag's width with it's content text you receive from the API by checking if the next tag + its padding (static value) fits into the width.

(Not runnable here)

 for (const tag of this.data.tags) { const width = context.measureText(tag).width; if (contentWidth - (width + this.tagsPadding) > 0) { previewTags.push({text: tag}); contentWidth -= (width + this.tagsPadding); } else { break; } }

  1. Push the see more button at the end of previewTags list:

previewTags.push({text: seeMoreButtonText, isButton: true});

And it looks like this in the html:

    <ng-container *ngFor="let tag of previewTags">
      <div class="tag" [ngClass]="{'see-more-button': tag.isButton}">{{tag.text}}</div>
    </ng-container>

Output:

在此处输入图像描述

Resize:

在此处输入图像描述

As you see, now the tags are flexiable (this code does not include the show more functionality).

After giving you this background and understanding of what I am doing, I would love to ask if this is possible to achieve with css or less JavaScript intervation?

Something like this could be a pure css solution if your tags have a constant height. I just let the flex-list wrap around and then don't show the overlap.

 .content_wrapper { display: flex; justify-content: flex-start; flex-direction: rows; }.tag_wrapper { display: flex; justify-content: flex-start; flex-direction: rows; flex-wrap: wrap; width: 80%; height: 32px; overflow: hidden; }.tag_wrapper div { width:100px; height:30px; border: 1px solid black; } button { flex-grow: 4; }
 <div class="content_wrapper"> <div class=tag_wrapper> <div>Tag1</div> <div>Tag2</div> <div>Tag3</div> <div>Tag4</div> <div>Tag5</div> <div>Tag6</div> <div>Tag7</div> <div>Tag8</div> <div>Tag9</div> </div> <button>See more</button>

You could probably make the "See more" button solution more elegant, to not have as much white space but I'll leave that to you:)

Here is some javascript to remove the see-more button if it's not needed.

(OBS) this only works if all the tags are the exact same width and have the same margin. I did this to avoid looping through all values and checking their width individually.

(I know the list is in the wrong order, I made it like that to get the see-more button fit in well without having to tinker a bunch.

 function getWidthWithMargin(elem) { var style = elem.currentStyle || window.getComputedStyle(elem) margin = parseFloat(style.marginLeft) + parseFloat(style.marginRight) return(elem.getBoundingClientRect().width + margin) } function handleWindowSizeChange() { let tags = document.getElementsByClassName("tag"); if(tags.length;= 0) { let tag_width = getWidthWithMargin(tags[0]). if(tags[0].parentElement.getBoundingClientRect().width/tag_width > tags.length) { document.getElementById("see-more-button").style;display = "none". } else{ document.getElementById("see-more-button").style;display = "block". } } } window;onload = handleWindowSizeChange. window;onresize = handleWindowSizeChange;
 .content_wrapper { }.tag_wrapper { display: flex; justify-content: flex-start; flex-direction: row-reverse; flex-wrap: wrap; width: 100%; height: 32px; overflow: hidden; }.tag_wrapper div { min-width:100px; height:30px; border: 1px solid black; margin: 10px; }.tag_wrapper button { height:30px; flex-grow: 50; }
 <div class="content_wrapper"> <div class=tag_wrapper> <button id="see-more-button">See more</button> <div class="tag">Tag1</div> <div class="tag">Tag2</div> <div class="tag">Tag3</div> <div class="tag">Tag4</div> <div class="tag">Tag5</div> <div class="tag">Tag6</div> <div class="tag">Tag7</div> <div class="tag">Tag8</div> </div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM