Los espacios vacíos son ignorados por la propiedad InnerText

8 minutos de lectura

Avatar de usuario de Arad
Arad

Estoy tratando de escribir una función (en JavaScript) que escribiría una oración en un <p> etiqueta escribiendo sus letras una por una con una pausa de 300 ms entre cada letra, por ejemplo. He escrito lo siguiente:

        var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", "y", "o", "u", "?"]
        function typeText() {
            var i = 0;
            var interval = setInterval(function () {
                var parag = document.getElementById("theParagraph");
                var paragOldText = parag.innerText;
                parag.innerText = paragOldText + text[i];
                i++;
                if (text.length == i)
                    clearInterval(interval);
            }, 200)
        }
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

Como puede ver, hay algunos caracteres ” ” (espacio vacío) en la matriz; el problema es que no escribe esos espacios vacíos, por lo que la oración quedaría así: “Hola como eres”. ¿Cómo puedo solucionar esto?

  • Como ninguna de las otras respuestas explica por qué tu código no funciona: en pocas palabras, es porque innerText representa el texto renderizado en una página, lo que significa aplicar el elemento white-space normas. El efecto en su caso es que se eliminan los espacios en blanco circundantes.

    – Konrad Rodolfo

    12 dic 2017 a las 10:40


  • Eche un vistazo a la diferencia entre el contenido del texto y el texto interno y posiblemente también esto

    – Bergi

    12 de diciembre de 2017 a las 14:36

  • Lee el comentario de Bergi^

    – Zack Plauché

    24 de junio de 2020 a las 6:05

No utilice la presentación como datos. Almacene el contenido actual como una cadena separada, no lo extraiga del DOM. De esta manera, no depende de cómo el navegador almacena el contenido de texto del elemento.

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", "y", "o", "u", "?"]
 
function typeText() {
    var i = 0;
    var paragText = "";
    var interval = setInterval(function () {
        var parag = document.getElementById("theParagraph");
        paragText += text[i];
        parag.innerText = paragText;
        i++;
        if (text.length == i)
            clearInterval(interval);
    }, 200)
}
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

Como nota al margen, lo mismo podría simplificarse mucho:

var text = "Hello how are you?";

function typeText() {
    var i = 0;
    var interval = setInterval(function () {
        var parag = document.getElementById("theParagraph");
        parag.innerText = text.substr(0, i);
        if (text.length == i)
            clearInterval(interval);
        i++;
    }, 200)
}
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

  • Si bien las otras respuestas son ciertamente correctas y contienen información útil, esta respuesta mencionó algo importante que ni siquiera se me ocurrió. gj

    – Juan Wu

    12 de diciembre de 2017 a las 10:31

Avatar de usuario de Gerardo Furtado
Gerardo Furtado

¿Qué pasa con el uso textContent?

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ","y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.textContent;
    parag.textContent = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

También puedes usar innerHTML:

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.innerHTML;
    parag.innerHTML = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

innerText fue presentado por IE y, como todos sabemos, nada bueno viene de IE. Bromas aparte, esta es una buena explicación al respecto: “El pobre e incomprendido texto interior”.

  • Hoy en día, todo lo que se escribe sobre la compatibilidad con los navegadores envejece bastante rápido, también FF se ha adaptado innerText últimamente.

    – Teemu

    12 de diciembre de 2017 a las 10:51

  • ¡Gracias! esto me ha ayudado. pero seria mejor que le digas la causa

    – Arad

    12 de diciembre de 2017 a las 12:14


  • @Teemu Más exactamente, ha sido adoptado por WHATWG últimamente.

    – Beto

    13 de diciembre de 2017 a las 2:16

Las otras respuestas abordan los problemas con su código, pero me gustaría abordar los problemas con todo su plan.

  • ¿Realmente quieres definir una serie de personajes? Las oraciones largas van a ser un infierno. ¿Y si quieres texto variable? Usa esto en su lugar:

    var input = "Hello how are you?";
    var text = input.split(""); // split into array of characters
    
  • Hablando de oraciones más largas, su “máquina de escribir” completará la línea actual, se dará cuenta de que no tiene espacio y luego bajará la última palabra a la siguiente línea para terminarla. ¡Esto no es un buen aspecto! Puedes evitar esto con un truco inteligente:

    <p><span id="visible_text">Hello how a</span><span id="remaining_text">re you?</span></p>
    <style>#remaining_text {visibility:hidden}</style>
    

    Esto no solo manejará muy bien el ajuste de palabras, sino que también “reservará” el espacio necesario con anticipación para que no termine empujando el contenido debajo de la máquina de escribir más abajo en la página a medida que surgen nuevas líneas.

    Puede lograr fácilmente este efecto contando en qué posición de personaje se encuentra y luego dividiendo el input cadena en dos piezas en ese desplazamiento. Pon la primera pieza en la primera. <span>el resto en el segundo, y estás dorado.

Fuente: uso esta técnica en mi código de estilo “RPG cutscene”. En realidad, una versión más avanzada, ya que la mía también es compatible con HTML en lugar de solo texto sin formato.

avatar de usuario de gurvinder372
gurvinder372

Necesitas introducir el espacio usando &nbsp; y use innerHTML en vez de innerText

var paragOldText = parag.innerHTML;
parag.innerHTML = paragOldText + ( text[i].trim().length ? text[i] : "&nbsp;" ) ;

Editar

&nbsp; no se requiere con innerHTML

var paragOldText = parag.innerHTML;
parag.innerHTML = paragOldText + text[i] ;

Manifestación

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", "y", "o", "u", "?"]

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.innerHTML;
    parag.innerHTML = paragOldText + text[i];
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

He modificado tu código para mostrar cómo puedes usar el método de corte para un código más corto y elegante.

var text = "Hello how are you?"
function typeText() {
var i = 0;
var parag = document.getElementById("theParagraph");
var interval = setInterval(function () {
    i++;
    parag.innerText = text.slice(0, i);
    if (i == text.length)
        clearInterval(interval);
    }, 200)
}
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

Avatar de usuario de Zack Plauché
Zack Plauche

Respuesta corta: Usar textContent atributo en lugar de innerText atributo y podrá agregar espacios.

p.ej

var text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", "?"] // Added missing space after "are"

function typeText() {
  var i = 0;
  var interval = setInterval(function() {
    var parag = document.getElementById("theParagraph");
    var paragOldText = parag.textContent; // Replaced "parag.innerText" with "parag.textContent"
    parag.textContent = paragOldText + text[i]; // Did it again.
    i++;
    if (text.length == i)
      clearInterval(interval);
  }, 200)
}
<body>
  <p id="theParagraph"></p>
  <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>
</body>

Además, tenga en cuenta que Konrad Rudolph y bergi respondieron el por qué en los comentarios directamente sobre la pregunta.

Avatar de usuario de Monilito Castro
monilito castro

Este problema es un gran candidato para un patrón MVC. Discuto este problema exacto en mi Blog. He proporcionado un MVC para este problema a continuación. (Disculpe la desvergonzada autopromoción).

const Model = function(){
   const self = this;
   self.index = 0;
   self.text = ["H", "e", "l", "l", "o", " ", "h", "o", "w", " ", "a", "r", "e", " ", "y", "o", "u", "?"];
   self.textString = "",
   self.accumulate = function(){
     const length = self.text.length;
     self.textString = self.textString + self.text[self.index];
     self.index = ++self.index % length;
   }
 }
  const Controller = function(model, elem, milsec){
   const self = this;
   self.elem = elem;
   self.start = function(){
     const interval = setInterval( function(){
      if(model.index===model.text.length-1){
       clearInterval(interval);
     }
       model.accumulate();
       self.elem.innerText = model.textString;   
     }, milsec);
   }
 }
 
 const typeText = function(){
   const model = new Model();
   const theParagraph = document.getElementById('theParagraph');
   const controller = new Controller(model, theParagraph, 200);
   controller.start();
 }
<body>
    <p id="theParagraph"></p>
    <button id="typeButton" onclick="typeText()" style="padding:15px">Start typing the sentence</button>

<p>
  I invite you to go to my <a target="_top" href="https://www.monilito.com/blog/Never-Use-Presentational-Structures-to-Store-State">blog article</a> for an interesting take on this problem.
</p>
</body>

  • Esta solución complica innecesariamente el código sin valor añadido, tratando de encajar una clavija cuadrada grande en un pequeño agujero redondo.

    – d4nyll

    10 jun 2021 a las 20:43

¿Ha sido útil esta solución?