How to prevent text jumping to new line when printing to div?

93 Views Asked by At

I've written a function for my little JavaScript project that prints text to a div akin to a dialog in an old-school game. I've included the code for this function in the snippet below, although I believe it's not really crucial for my question:

When I use this function, when a word is too long to fit in a line, the first few letters often are printed in the current line. Then the word jumps to another line and is finished there. This causes a rather unpleasant effect.

My question is, can I prevent this kind of behaviour through JS or CSS? Is there a way for the function to know in advance that the new word won't fit in the current line and start printing it in a new line from the beginning, thus avoiding the jump?

I tried various word-wrap settings for the target div, but to no avail. I don't really know which CSS property is responsible for this kind of behaviour. Thanks for your help.

I want to get rid of word jumping in words like 'demonstrates' or 'effect'. I would like them to be printed to a new line from the beginning.

Code Snippet (also on JSFiddle)

async function echo(targetId, msg) {
  let txtTarget = document.getElementById(targetId);
  for (let i = 0; i < msg.length; i++) {
    txtTarget.innerHTML += msg[i];
    txtTarget.scrollTop = txtTarget.scrollHeight;
    await this.sleep(55);
  }
}

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

const myMsg = 'This is a test message. I hope it clearly demonstrates what I\'m asking for. I want to get rid of the words jumping to new line effect. Instead, I want the words to be printed in a new line from the very beginning, if they\'re not going to fit in the current line.';

echo('message-box', myMsg);
body {
  background: black;
  color: ghostwhite;
}

#message-box {
  width: 300px;
  height: 200px;
  border: 2px solid ghostwhite;
}
<div id="message-box"></div>

2

There are 2 best solutions below

1
Josh Langley On

This method utilizes two elements. One is pre-filled with the entire message but uses transparent text. As each character is added to the "visible" element, the first character of the "invisible" element is removed. This back-fill prevents would-be-wrapped words from appearing on the wrong line.

async function echo(targetId, msg) {
    let txtTarget = document.getElementById(targetId);
    
    let visible = document.createElement('span');
    let invisible = document.createElement('span');
    
    invisible.style.color = 'transparent';
    
    txtTarget.appendChild(visible);
    txtTarget.appendChild(invisible);
    
    invisible.innerHTML = msg;

    for (let i = 0; i < msg.length; i++) {
        visible.innerHTML += msg[i];
        invisible.innerHTML = invisible.innerHTML.substring(1);
        txtTarget.scrollTop = txtTarget.scrollHeight;
        await this.sleep(55);
    }
}

function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
}

const myMsg = 'This is a test message. I hope it clearly demonstrates what I\'m asking for. I want to get rid of the words jumping to new line effect. Instead, I want the words to be printed in a new line from the very beginning, if they\'re not going to fit in the current line.';

echo('message-box', myMsg);
body {
  background: black;
  color: ghostwhite;
}

#message-box{
  width: 300px;
  height: 200px;;
  border: 2px solid ghostwhite;
}
<div id="message-box"></div>

2
A Haworth On

A simple (simplistic?) way would be to have just one instance of the string, in place, but in a span with opacity 0. Then on each clock tick move the span one character along.

const div = document.querySelector('div');
const string = "This is a test message. I hope it clearly demonstrates what I\'m asking for. I want to get rid of the words jumping to new line effect. Instead, I want the words to be printed in a new line from the very beginning, if they\'re not going to fit in the current line.";
const l = string.length;
div.innerHTML = '<span>' + string + '</span>';
let i = 0;
const interval = setInterval(function() {
    i++;
    if (i >= l) clearInterval(interval);
    div.innerHTML = string.substring(0, i) + '<span>' + string.substring(i, string.length) + '</span>';
  },
  55);
#message-box span {
  opacity: 0;
}

body {
  background: black;
  color: ghostwhite;
}

#message-box {
  width: 300px;
  height: 200px;
  border: 2px solid ghostwhite;
}
<style>
  #message-box span {
    opacity: 0;
  }
  
  body {
    background: black;
    color: ghostwhite;
  }
  
  #message-box {
    width: 300px;
    height: 200px;
    border: 2px solid ghostwhite;
  }
</style>
<div id="message-box"></div>