Search code examples
javascriptcsstext-formatting

Is there an option in CSS or JS to force single-character words at the end of a line to move to the next one?


While creating a responsive web design it often happens that a one-letter word remains as the last word in a paragraph's line:

This is a
paragraph

I would like to move such a single-letter word as follows:

This is
a paragraph

Is there any built-in property in CSS which allows to achieve this effect? If there is none, how can this be done in JavaScript?


Solution

  • In order to keep single-letter words (like 'a' or 'I') on the same line as the following word, you can replace the space character between them with the non-breaking space Unicode character \u00A0.

    This could be automated with a little JavaScript. For example, this code replaces [space][letter][space] with [space][letter][non-breaking space]:

    const modifySingleChars = str => str.replace(/ ([a-zA-Z]) /g,
        ' $1' + '\u00A0');
    

    To change all instances on a page, first collect all the text nodes within the body (skipping anything inside a <script> tag.

    Then simply iterate through the text nodes, making the appropriate substitutions.

    A working example:

    // form array of all text nodes in parentNode
    function allTextNodes(parentNode) {
      let arr = [];
      if (!parentNode) {
        return arr;
      }
    
      let nodes = parentNode.childNodes;
      nodes.forEach(node => {
        if (node.nodeName === 'SCRIPT') {
          return;
        }
        if (node.nodeType === Node.TEXT_NODE) {
          arr.push(node);
        } else {
          arr = arr.concat(allTextNodes(node));
        }
      });
      return arr;
    }
    
    // convert [space][letter][space] to [space][letter][non-breaking space];
    const modifySingleCharWords = str => str.replace(/ ([a-zA-Z]) /g,
      ' $1' + '\u00A0');
    
    function fixAllSingleCharWordsInBody() {
      let tNodes = allTextNodes(document.body);
      tNodes.forEach(tNode => {
        tNode.nodeValue = modifySingleCharWords(tNode.nodeValue);
      });
    }
    <body>
      <div id="wrapper" style="width:20rem">
        <h4>Prevent single character words at end of line</h4>
        <button type="button" onclick="fixAllSingleCharWordsInBody();">Fix All Words
      </button>
        <p>Lorem &nbsp;ipsum dolor i amet, consectetur a dipiscing elit, sed o eiusmod tempor incididunt u labore et dolore magna aliqua. <span>Nisl purus i mollis</span> nunc.
        </p>
        <p>In vitae turpis massa e elementum tempusus a sed. Eget mi proin e libero enim i faucibus. Quis lectus nulla a volutpat diam ut.
        </p>
        <p>Pharetra e ultrices neque ornare. Donec a tristique risus e feugiat in fermentum. Consectetur adipiscing e u aliquam purus sit amet.
        </p>
        <p>Vitae congue mauris rhoncus aenean e elit scelerisque mauris pellentesque. Mauris u eros i cursus turpis a tincidunt dui.
        </p>
        <p>At volutpat diam u venenatis tellus. Tellus integer feugiat scelerisque varius morbi i nunc faucibus at.</p>
        <script>
          const b = 'Do not modify anything inside a script tag';
        </script>
      </div>
    </body>