Accessing displayed hyphens of an HTML element with JavaScript

167 Views Asked by At

Given a

<div
  lang="en"
  style="
    overflow-wrap: word-break;
    hyphens: auto;
    width: 8.6em;
    background-color: highlight;
    color: highlightText;
    font-size: 20px;
  ">
    This is a text that is long enough
    to cause a line break if set so.
    By using words like
    "incomprehensibilities",
    we can demonstrate word breaks.
</div>

I would like to access the formatted text with hyphenations with JavaScript to get a string similar to:

"This is a text that is long enough to cause a line break if set so. By using words like "incomprehensibilit-ies", we can demon-strate word breaks.".

N.B. hyphens in incomprehensibilit-ies and demon-strate.

Is this possible?

The use case is that I need a way to break and hyphenate text in a <text> element of an SVG by only using SVG elements.

2

There are 2 best solutions below

0
Danny '365CSI' Engelman On

From the docs

Also read: https://www.brucebrotherton.com/blog/hyphens-on-the-web/


It is the browser in charge of rendering text, you have to analyze where text is rendered. With Canvas or SPAN

Far from perfect. This one:

  • wraps each word in a <span>
  • calculates by offsetHeight if a SPAN spans multiple lines
  • of so it found a hyphened word
  • it then removes each last character from the <span>
    to find when the word wrapped to a new line

The Far from perfect is the word "demonstrate" which "fits" when shortened to "demonstr" - "ate" But is not the English spelling

Needs some more JS voodoo

<style>
  .hyphen { background: pink }
  .remainder { background: lightblue }
</style>

<process-text lang="en" style="display:inline-block;
         overflow-wrap: word-break; hyphens: auto; zoom:1.2; width: 7em">
  By using words like
  "incomprehensibilities",
  we can demonstrate word breaks.
</process-text>

<script>
  customElements.define('process-text', class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => {
        let words = this.innerHTML.trim().split(/(\W+)/);
        let spanned = words.map(w => `<span>${w}</span>`).join('');
        this.innerHTML = spanned;
        let spans = [...this.querySelectorAll("span")];
        let defaultHeight = spans[0].offsetHeight;
        let hyphend = spans.map(span => {
          let hyphen = span.offsetHeight > defaultHeight;
          console.assert(span.offsetHeight == defaultHeight, span.innerText, span.offsetWidth);
          span.classList.toggle("hyphen", hyphen);
          if (hyphen) {
            let saved = span.innerText;
            while (span.innerText && span.offsetHeight > defaultHeight) {
              span.innerText = span.innerText.slice(0, -1);
            }
            let remainder = document.createElement("span");
            remainder.innerText = saved.replace(span.innerText, "");
            remainder.classList.add("remainder");
            span.after(remainder);
          }
        })
        console.log(this.querySelectorAll("span").length, "<SPAN> created" );
      }) //setTimeout to read innerHTML
    } // connectedCallback
  });
</script>

2
Vasily Liaskovsky On

Consider turning the problem around from "find out where the browser inserted line breaks" to "tell the browser to make specific line breaks".

This can be easily achieved by inserting soft hyphens (&shy;) at desired locations.