乐趣区

关于javascript:如何实现-textarea-的-autoHeight-功能

这个性能还比拟常见,用来获取文本的长宽(防止了计算不准的问题),次要用于实现 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

退出移动版