这个性能还比拟常见,用来获取文本的长宽(防止了计算不准的问题),次要用于实现 textarea 主动变长。

能够看到在咱们应用 textarea 的时候,有时候须要感知内容的高度,而后动静撑开。(elementUI 的 textarea 就提供了 autosize 的性能。)

那咱们也想实现这样的性能应该怎么办呢?

  1. 获取内容,而后统计字符个数估算。中文算两个,英文算一个。然而还是有问题的,比如说非等宽字体。
  2. 聪慧的读者曾经看到了咱们两头的 div 成果,就是咱们想要的高度。也是 elementUI 的计划,创立一个领有雷同款式的 div,而后获取他的高度

构建雷同款式的 DOM

看上去这个计划是最妙的。那么如何构建雷同的DOM呢?

  1. 既然要构建雷同的 DOM,那么咱们须要晓得 DOM 长什么样子
    那么如何获取款式呢?获取class?获取style?
    nonono,咱们要用 window.getComputedStyle(el),而后就能够高兴的拿到计算后的属性。
  2. 之后咱们须要晓得什么属性会影响字体排列
    CONTEXT_STYLE = [ 'letter-spacing', 'line-height', 'padding-top', 'padding-bottom', 'font-family', 'font-weight', 'font-size', 'text-rendering', 'text-transform', 'width', 'text-indent', 'padding-left', 'padding-right', 'border-width', 'box-sizing']
  3. 因为咱们须要从新搞一个DOM节点,而且咱们不心愿这个过程被用户看到,所以咱们要暗藏起来。有什么计划呢?

    • display:none 这个是不行的,因为 none 之后不会绘制了。也就获取不到宽高了。
    • opacity:0 这个能够
    • visibility: hidden; 这个也能够
    • height:0;overflow:hidden 这个也能够,获取scrollHeight
    • z-index:-999 这也能够的。
    • position:absolute;top:-9999px;left:-9999px 也是能够的

elementUI 实现

https://github.com/ElemeFE/element/blob/dev/packages/input/src/calcTextareaHeight.js

let hiddenTextarea;const HIDDEN_STYLE = `  height:0 !important;  visibility:hidden !important;  overflow:hidden !important;  position:absolute !important;  z-index:-1000 !important;  top:0 !important;  right:0 !important`;const CONTEXT_STYLE = [  'letter-spacing',  'line-height',  'padding-top',  'padding-bottom',  'font-family',  'font-weight',  'font-size',  'text-rendering',  'text-transform',  'width',  'text-indent',  'padding-left',  'padding-right',  'border-width',  'box-sizing'];function calculateNodeStyling(targetElement) {  const style = window.getComputedStyle(targetElement);  const boxSizing = style.getPropertyValue('box-sizing');  const paddingSize = (    parseFloat(style.getPropertyValue('padding-bottom')) +    parseFloat(style.getPropertyValue('padding-top'))  );  const borderSize = (    parseFloat(style.getPropertyValue('border-bottom-width')) +    parseFloat(style.getPropertyValue('border-top-width'))  );  const contextStyle = CONTEXT_STYLE    .map(name => `${name}:${style.getPropertyValue(name)}`)    .join(';');  return { contextStyle, paddingSize, borderSize, boxSizing };}export default function calcTextareaHeight(  targetElement,  minRows = 1,  maxRows = null) {  if (!hiddenTextarea) {    hiddenTextarea = document.createElement('textarea');    document.body.appendChild(hiddenTextarea);  }  let {    paddingSize,    borderSize,    boxSizing,    contextStyle  } = calculateNodeStyling(targetElement);  hiddenTextarea.setAttribute('style', `${contextStyle};${HIDDEN_STYLE}`);  hiddenTextarea.value = targetElement.value || targetElement.placeholder || '';  let height = hiddenTextarea.scrollHeight;  const result = {};  if (boxSizing === 'border-box') {    height = height + borderSize;  } else if (boxSizing === 'content-box') {    height = height - paddingSize;  }  hiddenTextarea.value = '';  let singleRowHeight = hiddenTextarea.scrollHeight - paddingSize;  if (minRows !== null) {    let minHeight = singleRowHeight * minRows;    if (boxSizing === 'border-box') {      minHeight = minHeight + paddingSize + borderSize;    }    height = Math.max(minHeight, height);    result.minHeight = `${ minHeight }px`;  }  if (maxRows !== null) {    let maxHeight = singleRowHeight * maxRows;    if (boxSizing === 'border-box') {      maxHeight = maxHeight + paddingSize + borderSize;    }    height = Math.min(maxHeight, height);  }  result.height = `${ height }px`;  hiddenTextarea.parentNode && hiddenTextarea.parentNode.removeChild(hiddenTextarea);  hiddenTextarea = null;  return result;};

微信公众号:前端linong