最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Find rendered line breaks with javascript - Stack Overflow

programmeradmin1浏览0评论

I have this situation :

div { width: 200px }
<div> example example example example example</div>

I have this situation :

div { width: 200px }
<div> example example example example example</div>

Text jumps to next line automatically when filling the full width of the <div>.

Using javascript how can I have the rendered content in the above line?

note: In the character string, there is no newline character

expected result from above snippet:

"example example example" corresponding with row 1 and "example example" corresponding with row 2

Share Improve this question edited Apr 10, 2019 at 5:34 Kaiido 137k14 gold badges256 silver badges320 bronze badges asked Apr 10, 2019 at 4:34 Tuan JihooTuan Jihoo 731 silver badge5 bronze badges 8
  • What do you mean by "cut" the text? What is your expected output looks like? – Ricky Mo Commented Apr 10, 2019 at 4:43
  • 2 Try css text wrap property. No need of javascript – Mayank Dudakiya Commented Apr 10, 2019 at 4:46
  • @RickyMo Right, As above, for example, the div extends over 2 rows. I only want to just get text in the row above – Tuan Jihoo Commented Apr 10, 2019 at 4:49
  • @MayankDudakiya i need get text in the row above and not want to hide the bottom line – Tuan Jihoo Commented Apr 10, 2019 at 4:52
  • Do you mean you have something like jsfiddle.net/c0tz35xv and try to get each rendered line (["This is", "some", "quite long" ...) ? – Kaiido Commented Apr 10, 2019 at 4:54
 |  Show 3 more comments

3 Answers 3

Reset to default 23

You can make use of the Range API and its handy getBoundingClientRect() method to determine which character marks the seizure in a TextNode.

Note that this obviously needs to be recalculated every time the window is resized / something changes the layout.

function getLineBreaks(node) {
  // we only deal with TextNodes
  if(!node || !node.parentNode || node.nodeType !== 3)
    return [];
  // our Range object form which we'll get the characters positions
  const range = document.createRange();
  // here we'll store all our lines
  const lines = [];
  // begin at the first char
  range.setStart(node, 0);
  // initial position
  let prevBottom = range.getBoundingClientRect().bottom;
  let str = node.textContent;
  let current = 1; // we already got index 0
  let lastFound = 0;
  let bottom = 0;
  // iterate over all characters
  while(current <= str.length) {
    // move our cursor
    range.setStart(node, current);
    if(current < str.length -1)
     range.setEnd(node, current+1);
    bottom = range.getBoundingClientRect().bottom;
    if(bottom > prevBottom) { // line break
      lines.push(
        str.substr(lastFound , current - lastFound) // text content
      );
      prevBottom = bottom;
      lastFound = current;
    }
    current++;
  }
  // push the last line
  lines.push(str.substr(lastFound));

  return lines;
}

console.log(getLineBreaks(document.querySelector('.test').childNodes[0]));
div.test {
  width: 50px;
  margin-bottom: 100px;
  word-break: break-all;
}

body>.as-console-wrapper{max-height:100px}
<div class="test">This is some quite long content that will wrap in multiple lines</div>

And if you need the relative y position of each lines:

function getLineBreaks(node) {
  // we only deal with TextNodes
  if(!node || !node.parentNode || node.nodeType !== 3)
    return [];
  // our Range object form which we'll get the characters positions
  const range = document.createRange();
  // here we'll store all our lines
  const lines = [];
  // begin at the first character
  range.setStart(node, 0);
  // get the position of the parent node so we can have relative positions later
  let contTop = node.parentNode.getBoundingClientRect().top;
  // initial position
  let prevBottom = range.getBoundingClientRect().bottom;
  let str = node.textContent;
  let current = 1; // we already got index 0
  let lastFound = 0;
  let bottom = 0;
  // iterate over all characters
  while(current <= str.length) {
    // move our cursor
    range.setStart(node, current);
    if(current < str.length - 1)
      range.setEnd(node, current+1); // wrap it (for Chrome...)
    bottom = range.getBoundingClientRect().bottom;
    if(bottom > prevBottom) { // line break
      lines.push({
        y: prevBottom - (contTop || 0), // relative bottom
        text: str.substr(lastFound , current - lastFound) // text content
      });
      prevBottom = bottom;
      lastFound = current;
    }
    current++;
  }
  // push the last line
  lines.push({
    y: bottom - (contTop || 0),
    text: str.substr(lastFound)
  });

  return lines;
}

console.log(getLineBreaks(document.querySelector('.test').childNodes[0]));
div.test {
  width: 50px;
  margin-bottom: 100px;
}

body>.as-console-wrapper{max-height:100px}
<div class="test">This is some quite long content that will wrap in multiple lines</div>

For the ones who need it to work over elements instead of a single text-node, here is a rewrite, which may very well fail (e.g with RTL direction) but which should be fine for most cases.

function getLineBreaks(elem) {
  // our Range object form which we'll get the characters positions
  const range = document.createRange();
  // here we'll store all our lines
  const lines = [];
  const nodes = grabTextNodes(elem);
  let left = 0;
  // get the position of the parent node so we can have relative positions later
  let contTop = nodes[0].parentNode.getBoundingClientRect().top;
  // initial position
  let prevLeft = null;
  let lineText = "";
  let startRange = null;
  for (const node of nodes) {
    let nodeText = node.textContent;
    const textLength = nodeText.length;
    let rangeIndex = 0;
    let textIndex = 0;
    while (rangeIndex <= textLength) {
      range.setStart(node, rangeIndex);
      if (rangeIndex < textLength - 1) {
        range.setEnd(node, rangeIndex + 1); // wrap the range (for Chrome...)
      }
      left = range.getBoundingClientRect().right;
      if (prevLeft === null) { // first pass
        prevLeft = left;
        startRange = range.cloneRange();
      } else if (left < prevLeft) { // line break
        // store the current line content
        lineText += nodeText.slice(0, textIndex);
        startRange.setEnd(range.endContainer, range.endOffset);
        const {
          bottom
        } = startRange.getBoundingClientRect();
        lines.push({
          y: bottom - contTop,
          text: lineText
        });
        // start a new line
        prevLeft = left;
        lineText = "";
        nodeText = nodeText.slice(textIndex);
        textIndex = 0;
        startRange = range.cloneRange();
      }
      rangeIndex++;
      textIndex++;
      prevLeft = left;
    }
    // add the remaining text from this node into the current line content
    lineText += nodeText;
  }
  // push the last line
  startRange.setEnd(range.endContainer, range.endOffset);
  const { bottom } = startRange.getBoundingClientRect();
  lines.push({
    y: bottom - contTop,
    text: lineText
  });
  return lines;
}

console.log(getLineBreaks(document.querySelector('.test')));

function grabTextNodes(elem) {
  const walker = document.createTreeWalker(elem, NodeFilter.SHOW_TEXT, null);
  const nodes = [];
  while (walker.nextNode()) {
    nodes.push(walker.currentNode);
  }
  return nodes;
}
div.test {
  width: 150px;
  margin-bottom: 100px;
}

.red {
  color: red;
}
<div class="test"><span class="red">This</span> is some quite long content that will wrap in <span class="red">mutiple</span> lines..</div>

This can be done using CSS. No Javascript required.

<div> example example example example example</div>
<style>
div{
    width: 200px;
    word-wrap: break-word;
}
</style>

Try CSS

div {
  width:200px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
发布评论

评论列表(0)

  1. 暂无评论