javascript - 使用 Javascript 将文本 chop 为 2 行

标签 javascript html css

下面我编写了一些代码,这些代码在表格单元格中获取一些内容,并在超出时将其 chop 为两行。当试图找到接近 2 整行的正确内容长度(但不会超过!)时,我采用对数方法(我认为)。我先把内容减半。然后我再次检查内容并添加或减去四分之一(一半的一半)。等等

要求:

  • chop 文本末尾的省略号 (...)。
  • 响应式,策略应该适用于动态宽度的单元格

问题:

  • 在代码片段中,我包含了一个生成 3 行的示例。我怎样才能保证我在 2 条线处着陆,同时合理地接近 2 条完整线?
  • 我采用了对数方法,因此我不必执行弹出一个词、重新测试、弹出一个词、重新测试等操作。这似乎仍然太昂贵,我该如何改进它?

document.querySelectorAll('.expand').forEach(td => {
  
  // get cell styles
  let styles = window.getComputedStyle(td);
  let lineHeight = parseInt(styles.lineHeight, 10);

  // create test element, mostly because td doesn't support max-height
  let el = document.createElement('div');
  el.innerHTML = td.innerHTML;
  el.style.maxHeight = (lineHeight * 2) + 'px';
  el.style.overflow = 'hidden';
  td.appendChild(el);

  // if scrollHeight is greater than clientHeight, we need to do some expand-y stuff
  if (el.scrollHeight > el.clientHeight) {
    
    // store content
    let content = el.innerHTML.trim(), 
        len = content.length;
    for (let i=Math.round(len*.5);; i=Math.round(i*.5)) {
      let over = el.scrollHeight > el.clientHeight;
      
      // if over 2 lines, cut by half
      // else increase by half
      over ? (len-=i) : (len+=i);
      
      // update innerHTML with updated content
      el.innerHTML = content.slice(0, len);
      
      console.log(i, len);
      
      // break if within margin of 10 and we landed under
      if (i<10 && !over) break;
    }
    
    td.innerHTML = `
      <div class="hide-expanded">${el.innerHTML.slice(0, -3).trim()}...</div>
      <div class="show-expanded">${content}</div>
      <button type="button">Toggle</button>`;
    
    td.querySelector('button').addEventListener('click', e => td.classList.toggle('expanded'))
  }
});
html {
  font-size: 14px;
  line-height: 24px;
  font-family: Helvetica, Arial, sans-serif;
}

table {
  border-collapse: collapse;
}

td {
  white-space: nowrap;
  padding: 1rem;
}

.expand {
  white-space: normal;
}

.expand:not(.expanded) .show-expanded,
.expand.expanded .hide-expanded {
  display: none;
}
<table>
  <tbody>
    <tr>
      <td class="expand">This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content. This is some content.</td>
    </tr>
  </tbody>
</table>

最佳答案

这个 github 存储库是我能找到的最好(也是最简洁)的解决方案。我已经从中改编了一个解决方案。

https://github.com/dollarshaveclub/shave/blob/master/src/shave.js

export default function shave(target, maxHeight, opts) {
  if (!maxHeight) throw Error('maxHeight is required');
  let els = typeof target === 'string' ? document.querySelectorAll(target) : target;
  if (!('length' in els)) els = [els];

  const defaults = {
    character: '…',
    classname: 'js-shave',
    spaces: true,
  };
  const character = opts && opts.character || defaults.character;
  const classname = opts && opts.classname || defaults.classname;
  const spaces = opts && opts.spaces === false ? false : defaults.spaces;
  const charHtml = `<span class="js-shave-char">${character}</span>`;

  for (let i = 0; i < els.length; i++) {
    const el = els[i];
    const span = el.querySelector(`.${classname}`);

    // If element text has already been shaved
    if (span) {
      // Remove the ellipsis to recapture the original text
      el.removeChild(el.querySelector('.js-shave-char'));
      el.textContent = el.textContent; // nuke span, recombine text
    }

    // If already short enough, we're done
    if (el.offsetHeight < maxHeight) continue;

    const fullText = el.textContent;
    const words = spaces ? fullText.split(' ') : fullText;

    // If 0 or 1 words, we're done
    if (words.length < 2) continue;

    // Binary search for number of words which can fit in allotted height
    let max = words.length - 1;
    let min = 0;
    let pivot;
    while (min < max) {
      pivot = (min + max + 1) >> 1;
      el.textContent = spaces ? words.slice(0, pivot).join(' ') : words.slice(0, pivot);
      el.insertAdjacentHTML('beforeend', charHtml);
      if (el.offsetHeight > maxHeight) max = spaces ? pivot - 1 : pivot - 2;
      else min = pivot;
    }

    el.textContent = spaces ? words.slice(0, max).join(' ') : words.slice(0, max);
    el.insertAdjacentHTML('beforeend', charHtml);
    const diff = spaces ? words.slice(max + 1).join(' ') : words.slice(max);

    el.insertAdjacentHTML('beforeend',
      `<span class="${classname}" style="display:none;">${diff}</span>`);
  }
}

关于javascript - 使用 Javascript 将文本 chop 为 2 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40351172/

相关文章:

javascript - 如何在点击时切换元素的innerHTML

html - 如何使用对象中给出的数据构建动态 a href 链接(Node.js && handle )

javascript - 如何刷新另一个页面内的页面?

html - 显示银条的渐变图像

css - anchor 不会在列表项内垂直居中

javascript - 没有按钮无法暂停轮播

javascript - 在javascript中用另一个替换重复的数组对象值

javascript - 如何仅在单击子项时触发父级单击事件

javascript - 当位置设置为相对时网页上的重叠表

javascript - 具有单独切换的多个显示/隐藏 div