Multiline content exceeds JS solution displayed

A seemingly simple problem, but it is not easy to write - for multiline text, beyond Display.

It can be implemented through css, but limited by browser compatibility, it sometimes needs to rely on js.

Through js implementation, we need to take into account the text size. The byte lengths corresponding to Chinese and English, numbers and punctuation symbols are inconsistent. If the consideration is not comprehensive, there will always be a gap for different text contents.

First of all, we need to understand that the byte length occupied by Chinese characters, English letters, numbers and special symbols is different. If it needs to be calculated accurately, it cannot be intercepted according to the number of elements of the string. They can be intercepted by converting them into the number of bytes, which has higher accuracy. Therefore, we need a method to obtain the length of string bytes:

function bitCompute(content) {
   var total = 0,
    len = arguments[0].length || 0
   for (var i = 0; i < len; i++) {
    if (content[i].charCodeAt() > 255) {
     total += 2;
    } else {
     total += 1;
    }
   }
   return total
}

For the number of bytes of content to be intercepted, we need to know the ratio of the number of bytes that can be put into the container to the total number of bytes. Show the number of bytes / total bytes = offsetWidth / scrollWidth:

function complate() {
   var offsetWidth = el.offsetWidth;
   var scrollWidth = el.scrollWidth;
   var gap = scrollWidth - offsetWidth;
   var percent = Math.floor(offsetWidth / scrollWidth * 1e3) / 1e3;
   return {
    gap: gap,
    percent: percent
   }
}

According to the calculated data, we can manipulate the string.

function cut(content) {
   el.innerhtml = content;
   var info = complate(),
    percent = info.percent;
   var total = bitCompute(content).total;
   var showLen = +(total * percent).toFixed(0) - cfg.placeholder;
   content = bitCompute(content, showLen).content;
   return content + cfg.padding;
}
  
function bitCompute(content, maxLen) {
   var total = 0,
    len = arguments[0].length || 0,
    outContent = '';
   for (var i = 0; i < len; i++) {
    if (content[i].charCodeAt() > 255) {
     total += 2;
    } else {
     total += 1;
    }
    if (maxLen && total > maxLen) {
     break;
    }
    outContent += content[i];
   }
   return {
    total: total,
    content: outContent
   }
}

Of course, the amount of text displayed is also related to the font size, so we also need to take the factor of font size into account. As a working method, it should not be associated with the elements in the page. Therefore, we should create elements in the method, put in the content, and calculate offsetWidth and scrollWidth.

function cutFactory(opt) {
  var cfg = {
   padding: opt.padding || "...",
   classList: opt.classList || [],
   style: opt.style || {},
   debug: opt.debug
  };
  cfg.placeholder = bitCompute(cfg.padding).total;
  var el = doc.createElement("span");
  el.className = cfg.classList.join(" ");
  var customStyles = [];
  for (var styleKey in cfg.style) {
   if (cfg.style.hasOwnProperty(styleKey)) {
    customStyles.push(styleKey + ":" + cfg.style[styleKey]);
   }
  }
  el.style.cssText = "position:absolute;left:0;top:0;background:transparent;color:transparent;height:100%;white-space:nowrap;overflow:visible;border:0;" + (cfg.debug ? "background:white;color:red;" : "") + customStyles.join(";");
  var div = doc.createElement("div");
  div.appendChild(el);
  div.style.cssText = "width:99%;min-height:50px;line-height:50px;position:absolute;left:3px;top:3px;overflow:hidden;outline:0;background:transparent;" + (cfg.debug ? "outline:1px solid red;background:black;" : "");
  doc.body.appendChild(div);
  var css = win.getComputedStyle(el);
  cfg.fontSize = parseFloat(css.fontSize) || 16;
  return function (content) {
   el.innerhtml = content;
   var out = {
    flag: false,
    cut: '',
    all: content,
    last: content
   }
   if (complate().gap > 0) {
    out.flag = true,
    out.last = out.cut = cut(content)
   }
   return out
  }
}

Finally, another method is exposed to facilitate user invocation.

For performance reasons, instead of creating too many dom elements, we can cache interception methods with the same font size and container width.

function subStringEL(name, fontSize, width) {
  this.subStringELFns || (this.subStringELFns = {});
  var key = 'key_' + fontSize + '_' + width;
  var fn = this.subStringELFns[key];
  if (!fn) {
   fn = this.subStringELFns[key] = cutFactory({
    style: {
     'font-size': fontSize,
     'width': width
    }
   })
  }
  return fn(name);
}

This perfectly solves the problem of multiple lines exceeding Display problems, good compatibility, but also accurate interception, flexible and convenient.

For the above content shared with you, if you need to learn tutorials, source code notes or want to learn and communicate, scan the code and add me to pull you into the group

Keywords: Javascript Front-end Vue.js

Added by mrchuckles2002 on Sun, 26 Dec 2021 10:18:23 +0200