Home >Web Front-end >JS Tutorial >Handle multi-line text overflow by encapsulating a v-clamp instruction

Handle multi-line text overflow by encapsulating a v-clamp instruction

不言
不言forward
2018-10-26 15:24:573529browse

The content of this article is about processing the overflow of multi-line text by encapsulating a v-clamp instruction. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

When I was working on a project recently, I encountered a requirement: the text in the div is required to be displayed on two lines. The width of the div is fixed. If it overflows, an ellipsis will be displayed. We are all familiar with the overflow problem of single-line text. Just add the following css attribute and it will be ok:

  overflow: hidden;
  white-space: nowrap; //段落中文本不换行
  text-overflow: ellipsis;

But how to deal with the overflow of multi-line text?

After checking the information, I found that there is still a way

It is relatively simple to implement the page in WebKit browser or mobile terminal (most browsers with WebKit core), you can use WebKit directly CSS extended properties (WebKit is a private property)-webkit-line-clamp ;Note: This is an unsupported WebKit property and does not appear in CSS in draft specification. -webkit-line-clamp is used to limit the number of lines of text displayed in a block element. In order to achieve this effect, it needs to be combined with other WebKit properties.

We can achieve it with the following code:

    overflow : hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;

However, this method will encounter compatibility issues and will not work on the Firefox browser.

In order to solve the compatibility problem, there is a clamp.js[https://www.npmjs.com/package...] that solves this problem very well.

In order to better integrate with Vue, today we will encapsulate a v-clamp instruction to conveniently solve this problem.

// 注册一个全局自定义指令 `v-clamp`
Vue.directive('clamp', {
  // 当被绑定的元素插入到 DOM 中时……
  update: function (el, binding) {
      function clamp(element, options) {
          options = options || {};
      
          var self = this,
            win = window,
            opt = {
              clamp: options.clamp || 2,
              useNativeClamp: typeof(options.useNativeClamp) != 'undefined' ? options.useNativeClamp : true,
              splitOnChars: options.splitOnChars || ['.', '-', '–', '—', ' '], //Split on sentences (periods), hypens, en-dashes, em-dashes, and words (spaces).
              animate: options.animate || false,
              truncationChar: options.truncationChar || '…',
              truncationHTML: options.truncationHTML
            },
      
            sty = element.style,
            originalText = element.innerHTML,
      
            supportsNativeClamp = typeof(element.style.webkitLineClamp) != 'undefined',
            clampValue = opt.clamp,
            isCSSValue = clampValue.indexOf && (clampValue.indexOf('px') > -1 || clampValue.indexOf('em') > -1),
            truncationHTMLContainer;
      
          if (opt.truncationHTML) {
            truncationHTMLContainer = document.createElement('span');
            truncationHTMLContainer.innerHTML = opt.truncationHTML;
          }
      
      
          // UTILITY FUNCTIONS __________________________________________________________
      
          /**
           * Return the current style for an element.
           * @param {HTMLElement} elem The element to compute.
           * @param {string} prop The style property.
           * @returns {number}
           */
          function computeStyle(elem, prop) {
            if (!win.getComputedStyle) {
              win.getComputedStyle = function(el, pseudo) {
                this.el = el;
                this.getPropertyValue = function(prop) {
                  var re = /(\-([a-z]){1})/g;
                  if (prop == 'float') prop = 'styleFloat';
                  if (re.test(prop)) {
                    prop = prop.replace(re, function() {
                      return arguments[2].toUpperCase();
                    });
                  }
                  return el.currentStyle && el.currentStyle[prop] ? el.currentStyle[prop] : null;
                };
                return this;
              };
            }
      
            return win.getComputedStyle(elem, null).getPropertyValue(prop);
          }
      
          /**
           * Returns the maximum number of lines of text that should be rendered based
           * on the current height of the element and the line-height of the text.
           */
          function getMaxLines(height) {
            var availHeight = height || element.clientHeight,
              lineHeight = getLineHeight(element);
      
            return Math.max(Math.floor(availHeight / lineHeight), 0);
          }
      
          /**
           * Returns the maximum height a given element should have based on the line-
           * height of the text and the given clamp value.
           */
          function getMaxHeight(clmp) {
            var lineHeight = getLineHeight(element);
            return lineHeight * clmp;
          }
      
          /**
           * Returns the line-height of an element as an integer.
           */
          function getLineHeight(elem) {
            var lh = computeStyle(elem, 'line-height');
            if (lh == 'normal') {
              // Normal line heights vary from browser to browser. The spec recommends
              // a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff.
              lh = parseInt(computeStyle(elem, 'font-size')) * 1.2;
            }
            return parseInt(lh);
          }
      
      
          // MEAT AND POTATOES (MMMM, POTATOES...) ______________________________________
          var splitOnChars = opt.splitOnChars.slice(0),
            splitChar = splitOnChars[0],
            chunks,
            lastChunk;
      
          /**
           * Gets an element's last child. That may be another node or a node's contents.
           */
          function getLastChild(elem) {
            //Current element has children, need to go deeper and get last child as a text node
            if (elem.lastChild.children && elem.lastChild.children.length > 0) {
              return getLastChild(Array.prototype.slice.call(elem.children).pop());
            }
            //This is the absolute last child, a text node, but something's wrong with it. Remove it and keep trying
            else if (!elem.lastChild || !elem.lastChild.nodeValue || elem.lastChild.nodeValue === '' || elem.lastChild.nodeValue == opt.truncationChar) {
              elem.lastChild.parentNode.removeChild(elem.lastChild);
              return getLastChild(element);
            }
            //This is the last child we want, return it
            else {
              return elem.lastChild;
            }
          }
      
          /**
           * Removes one character at a time from the text until its width or
           * height is beneath the passed-in max param.
           */
          function truncate(target, maxHeight) {
            if (!maxHeight) {
              return;
            }
      
            /**
             * Resets global variables.
             */
            function reset() {
              splitOnChars = opt.splitOnChars.slice(0);
              splitChar = splitOnChars[0];
              chunks = null;
              lastChunk = null;
            }
      
            var nodeValue = target.nodeValue.replace(opt.truncationChar, '');
      
            //Grab the next chunks
            if (!chunks) {
              //If there are more characters to try, grab the next one
              if (splitOnChars.length > 0) {
                splitChar = splitOnChars.shift();
              }
              //No characters to chunk by. Go character-by-character
              else {
                splitChar = '';
              }
      
              chunks = nodeValue.split(splitChar);
            }
      
            //If there are chunks left to remove, remove the last one and see if
            // the nodeValue fits.
            if (chunks.length > 1) {
              // console.log('chunks', chunks);
              lastChunk = chunks.pop();
              // console.log('lastChunk', lastChunk);
              applyEllipsis(target, chunks.join(splitChar));
            }
            //No more chunks can be removed using this character
            else {
              chunks = null;
            }
      
            //Insert the custom HTML before the truncation character
            if (truncationHTMLContainer) {
              target.nodeValue = target.nodeValue.replace(opt.truncationChar, '');
              element.innerHTML = target.nodeValue + ' ' + truncationHTMLContainer.innerHTML + opt.truncationChar;
            }
      
            //Search produced valid chunks
            if (chunks) {
              //It fits
              if (element.clientHeight <= maxHeight) {
                //There&#39;s still more characters to try splitting on, not quite done yet
                if (splitOnChars.length >= 0 && splitChar !== '') {
                  applyEllipsis(target, chunks.join(splitChar) + splitChar + lastChunk);
                  chunks = null;
                }
                //Finished!
                else {
                  return element.innerHTML;
                }
              }
            }
            //No valid chunks produced
            else {
              //No valid chunks even when splitting by letter, time to move
              //on to the next node
              if (splitChar === '') {
                applyEllipsis(target, '');
                target = getLastChild(element);
      
                reset();
              }
            }
      
            //If you get here it means still too big, let's keep truncating
            if (opt.animate) {
              setTimeout(function() {
                truncate(target, maxHeight);
              }, opt.animate === true ? 10 : opt.animate);
            } else {
              return truncate(target, maxHeight);
            }
          }
      
          function applyEllipsis(elem, str) {
            elem.nodeValue = str + opt.truncationChar;
          }
      
      
          // CONSTRUCTOR ________________________________________________________________
      
          if (clampValue == 'auto') {
            clampValue = getMaxLines();
          } else if (isCSSValue) {
            clampValue = getMaxLines(parseInt(clampValue));
          }
      
          var clampedText;
          if (supportsNativeClamp && opt.useNativeClamp) {
            sty.overflow = 'hidden';
            sty.textOverflow = 'ellipsis';
            sty.webkitBoxOrient = 'vertical';
            sty.display = '-webkit-box';
            sty.webkitLineClamp = clampValue;
      
            if (isCSSValue) {
              sty.height = opt.clamp + 'px';
            }
          } else {
            var height = getMaxHeight(clampValue);
            if (height <= element.clientHeight) {
              console.log(getLastChild(element));
              clampedText = truncate(getLastChild(element), height);
            }
          }
      
          return {
            &#39;original&#39;: originalText,
            &#39;clamped&#39;: clampedText
          };
        }

       clamp(el,{clamp: 2}) 


  }
})

In fact, it is very simple. It just moves the functions in clamp.js. Then you can use it like this:

  <div class="txt" v-clamp>很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板</div>

The above is the detailed content of Handle multi-line text overflow by encapsulating a v-clamp instruction. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete