moluy

这个功能还比较常见,用来获取文本的长宽(避免了计算不准的问题),主要用于实现 textarea 自动变长。

可以看到在我们使用 textarea 的时候,有时候需要感知内容的高度,然后动态撑开。(elementUI 的 textarea 就提供了 autosize 的功能。)

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

 

获取内容,然后统计字符个数估算。中文算两个,英文算一个。但是还是有问题的,比如说非等宽字体。

聪明的读者已经看到了我们中间的 div 效果,就是我们想要的高度。也是 elementUI 的方案,创建一个拥有相同样式的 div,然后获取他的高度

 

 

构建相同样式的 DOM

看上去这个方案是最妙的。那么如何构建相同的DOM呢?

既然要构建相同的 DOM,那么我们需要知道 DOM 长什么样子

那么如何获取样式呢?获取class?获取style?

nonono,我们要用 window.getComputedStyle(el),然后就可以快乐的拿到计算后的属性。

之后我们需要知道什么属性会影响字体排列

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\']

 

因为我们需要重新搞一个DOM节点,而且我们不希望这个过程被用户看到,所以我们要隐藏起来。有什么方案呢?

 

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

 

https://www.houdianzi.com/cslogo/ 长沙logo设计

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;
};

分类:

技术点:

相关文章: