basement
basement

Reputation: 718

HTML isn't interpreted correctly when injecting '<br>' strings into innerHTML

'use strict';                                     

function injectCharacters(){
  var paragraph = document.getElementById( 'paragraph' ),
      text = paragraph.innerHTML,
      ary = text.split( '' ),
      i = 1;
  
  paragraph.innerHTML = '';
  
  for ( i; i <= 52 ; i++ ){
    window.setTimeout(
      ( 
        function( i ){ 
          return function(){
            paragraph.innerHTML +=  ary[ i ];
          }
        } 
      )
      ( i ), i * 100 
    )
  }
}

injectCharacters();
* {
  margin: 0;
}
html,
body {
  overflow: hidden;
  height: 100%;
}
body {
  display: flex;
  font-family: Arial;
}
p {
  margin: auto;
}
.mtx-3d {
  transform:
    perspective( 10rem )
    matrix3D( 
      1, 0.25, 0.5, 0.0075,
      0.25, 2, 2, 0.02,
      1, 2, 3, 4,
      1, 2, 3, 0.75 
    )
}
<p id="paragraph" class="mtx-3d">
  So. Yeah I mean. <br>I've been thinking <br>A lot
</p>

In the snippet above the line breaks are literal in the output. I want an actual line-break to display instead of seeing <br>. How can I solve this simple problem?

I suspect it has something to do with me breaking up every character into a value housed in an array then outputting that back to the paragraph element but I could be wrong.

Upvotes: 1

Views: 971

Answers (3)

Patrick Evans
Patrick Evans

Reputation: 42746

You will need to split the html element away into it's own element so it will end up like

["a,"b","c","<br>","d","e","f"]

You could for instance loop over each node of the element, split all textNodes and when it sees a element just add the element's html.

Array.from(paragraph.childNodes).forEach(function(node){
  if(node.nodeType == 3){
    ary.push( ...node.textContent.split('') );
  } else {
    ary.push( node.outerHTML );
  }
});

Note this example will probably only work well with elements that don't have sub elements like a div with children. And if your paragraph ends up containing more complex html than just <br> it will probably need refactoring.

Demo

'use strict';                                     

function start(){
  var paragraph = document.getElementById( 'paragraph' ),
      ary = [];
  Array.from(paragraph.childNodes).forEach(function(node){
    if(node.nodeType == 3){
      ary.push( ...node.textContent.split('') );
    } else {
      ary.push( node.outerHTML );
    }
  });

  paragraph.innerHTML = '';
  
  for ( let i=1; i < ary.length ; i++ ){
    window.setTimeout(function(){ 
      paragraph.innerHTML +=  ary[ i ];
    }, i * 100);
  }
}

start();
* {
  margin: 0;
}
html,
body {
  overflow: hidden;
  height: 100%;
}
body {
  display: flex;
  font-family: Arial;
}
p {
  margin: auto;
}
.mtx-3d {
  transform:
    perspective( 10rem )
    matrix3D( 1, 0.25, 0.5, 0.0075, 0.25, 2, 2, 0.02, 1, 2, 3, 4, 1, 2, 3, 0.75 )
}
<p id="paragraph" class="mtx-3d">
  So. Yeah I mean. <br>I've been thinking <br>A lot
</p>

Upvotes: 1

Anthony McGrath
Anthony McGrath

Reputation: 802

This seemed to work for me:

'use strict';                                     

function injectCharacters() {
  var paragraph = document.getElementById('paragraph'),
      text = paragraph.innerHTML,
      i = 0;

  paragraph.innerHTML = '';

  while (i < text.length) {                                 
    var c = text.charAt(i);
    if (text.slice(i, i + 4) == '<br>') {
        c = text.slice(i, i + 5);
    }
    window.setTimeout(function( c ) { 
        return function() {
          paragraph.innerHTML += c;
        }
      }( c ), i * 100 
    ); 
    i += c.length;
  }
}

injectCharacters();

I basically re-worked the logic to check for '<br>'. If found, append it plus the next character. This seems to preserve the <br> element in the html.

Upvotes: 1

Kaiido
Kaiido

Reputation: 137171

By appending a new character every time, with innerHTML += the browser will convert < and > characters as HTMLEntities, and thus the sequence <br> will be converted to &lt;br&gt; :

para.innerHTML += '<';
console.log(para.innerHTML);
<p id="para"></p>

To fix it, you can set the current content every time with innerHTML = text.slice(0, i);.

'use strict';                                     

function injectCharacters(){
  var paragraph = document.getElementById( 'paragraph' ),
      text = paragraph.innerHTML,
      i = 1;
  
  paragraph.innerHTML = '';
  
  for ( i; i <= 52 ; i++ ){
    window.setTimeout(
      ( 
        function( i ){ 
          return function(){
            paragraph.innerHTML =  text.slice(0,i);
          }
        } 
      )
      ( i ), i * 100 
    )
  }
}

injectCharacters();
* {
  margin: 0;
}
html,
body {
  overflow: hidden;
  height: 100%;
}
body {
  display: flex;
  font-family: Arial;
}
p {
  margin: auto;
}
.mtx-3d {
  transform:
    perspective( 10rem )
    matrix3D( 
      1, 0.25, 0.5, 0.0075,
      0.25, 2, 2, 0.02,
      1, 2, 3, 4,
      1, 2, 3, 0.75 
    )
}
<p id="paragraph" class="mtx-3d">
  So. Yeah I mean. <br>I've been thinking <br>A lot
</p>

And if you don't want to show <, <b and <br, you could replace it in your string before hand to some unlikely to show up character, and replace it again when setting :

'use strict';                                     

function injectCharacters(){
  var paragraph = document.getElementById( 'paragraph' ),
      text = paragraph.innerHTML.replace(/<br>/g, '\u10FF'),
      i = 1;
  
  paragraph.innerHTML = '';
  
  for ( i; i <= 52 ; i++ ){
    window.setTimeout(
      ( 
        function( i ){ 
          return function(){
            paragraph.innerHTML =  text.slice(0,i).replace(/\u10FF/g, '<br>');
          }
        } 
      )
      ( i ), i * 100 
    )
  }
}

injectCharacters();
* {
  margin: 0;
}
html,
body {
  overflow: hidden;
  height: 100%;
}
body {
  display: flex;
  font-family: Arial;
}
p {
  margin: auto;
}
.mtx-3d {
  transform:
    perspective( 10rem )
    matrix3D( 
      1, 0.25, 0.5, 0.0075,
      0.25, 2, 2, 0.02,
      1, 2, 3, 4,
      1, 2, 3, 0.75 
    )
}
<p id="paragraph" class="mtx-3d">
  So. Yeah I mean. <br>I've been thinking <br>A lot
</p>

Upvotes: 1

Related Questions