Эффект пишущей машинки Javascript из дочерних узлов элемента

#javascript #jquery #html

#javascript #jquery #HTML

Вопрос:

Я пытаюсь создать эффект записи типов, который будет получать узлы элемента, а затем отображать значения этих узлов последовательно с заданной скоростью. Если узел является текстовым узлом, я хочу, чтобы он входил и последовательно отображал каждый символ в этом тексте.

HTML:

 <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

<!-- item will be appened to this layout -->
<div id="log" class="sl__chat__layout">
</div>


<!-- chat item -->
<script type="text/template" id="chatlist_item">

  <div data-from="{from}" data-id="{messageId}" id="messageID">
    <div id="messageBox">

    <span id="message">
      {message}
    </span>

    </div>
  </div>
</script>

  

Javascript:

 // Please use event listeners to run functions.
document.addEventListener('onLoad', function(obj) {
    // obj will be empty for chat widget
    // this will fire only once when the widget loads
});

document.addEventListener('onEventReceived', function(obj) {
    // obj will contain information about the event
e    
typeEffect(e);  
});

var speed = 50;
var e = 1;

function typeEffect(inp) {
    var o = inp;
    document.getElementById("messageID").id= "messageID" o;
    document.getElementById("message").id= "message" o;
    var text = $("#message" o).text();
    $("#message" o).text('');

    var i = 0;
    var timer = setInterval(function() {
        if(i < text.length) {
            $("#message" o).append(text.charAt(i));
            i  ;
        }

        else{
            clearInterval(timer);
        };   
  }, speed);

    }                    
  

Вот пример элемента с идентификатором «message2». Как вы можете видеть, он содержит некоторый текст, затем интервал, содержащий изображение, а затем еще немного текста.

    <span id="message2">
      Hello 
      <span class="emote">
         <img src="https://static-cdn.jtvnw.net/emoticons/v1/1251411/1.0"> 
      </span> 
      There
    </span>
  

В моем коде, опубликованном выше, я могу создать эффект пишущей машинки для текста. Однако, используя приведенный выше пример, я не могу придумать способ ввести «Hello», затем интервал с изображением, а затем «There».

Я пытался получить узлы, подобные этому:

    var contents = document.getElementById("message" o).childNodes;
  

Когда я регистрирую это в консоли, я получаю: NodeList (3) [текст, span.emote, текст]

Однако оттуда у меня возникают проблемы с доступом к значениям nodeValues. Я продолжаю получать ошибки. Я не уверен, что именно я делаю неправильно. Оттуда я также не уверен, что правильный способ очистить элемент «message» o, а затем заполнить его информацией.

Надеюсь, это все объясняет!

Ответ №1:

При использовании $.text() вы получаете свой элемент textContent , и все содержимое его разметки исчезает (фактически все его дочерние узлы).

Чтобы сохранить это содержимое, вам нужно сохранить узлы DOM, а не только их textContent .
Оттуда вам нужно будет отсоединить DOM-дерево и обойти его, добавляя каждый элемент, медленно повторяя каждый текстовый узел textContent .

Однако сделать это не так просто. Действительно, тот факт, что мы повторно добавим узлы DOM внутри документа, означает, что отдельное дерево DOM, по которому мы шли, будет сломано.

Чтобы обойти это, нам, таким образом, нужно создать копию отдельного DOM-дерева, которую мы сохраним нетронутой, чтобы мы могли продолжать обходить его точно так же, как если бы оно было оригинальным.
И для того, чтобы знать, где разместить наши элементы, нам нужно сохранить каждый исходный узел как свойство клонированного.

Для этого мы создадим два TreeWalkers, один для исходных узлов, а другой для клонированной версии. Используя оба параметра одновременно, мы можем легко установить .original свойство наших клонов.
Затем нам просто нужно вернуться к корню нашего clones TreeWalker и начать снова обходить его, на этот раз имея возможность добавить правильный узел к его исходному родительскому узлу.

 async function typeWrite(root, freq) {
  // grab our element's content
  const content = [...root.childNodes];
  // move it to a documentFragment
  const originals = document.createDocumentFragment();
  originals.append.apply(originals, content);
  // clone this documentFragment so can keep a clean version of the DOM tree
  const clones = originals.cloneNode(true);
  // every clone will have an `original` node
  // clones documentFragment's one is the root Element, still in doc
  clones.original = root;
  
  // make two TreeWalkers
  const originals_walker = document.createTreeWalker(originals, NodeFilter.SHOW_ALL, null);
  const clones_walker = document.createTreeWalker(clones, NodeFilter.SHOW_ALL, null);

  while(originals_walker.nextNode() amp;amp; clones_walker.nextNode()) {
    // link each original node to its clone
    clones_walker.currentNode.original = 
      originals_walker.currentNode
  }
  while(clones_walker.parentNode()) {
    // go back to root
  }
  // walk down only our clones (will stay untouched now)
  while(clones_walker.nextNode()) {
  
    const clone = clones_walker.currentNode;
    const original = clone.original;
    // retrieve the original parentNode (which is already in doc)
    clone.parentNode.original
      .append(original); // and append the original version of our currentNode
      
    if(clone.nodeType === 3) { // TextNode
      const originalText = original.textContent;
      // we use a trimmed version to avoid all non visible characters
      const txt = originalText.trim().replace(/n/g, '');
      original.textContent = ''; // in doc => empty for now
      let i = 0;
      while(i < txt.length) {
        await wait(freq); // TypeWriting effect...
        original.textContent  = txt[i  ];
      }
      // restore original textContent (invisible to user)
      original.textContent = originalText;
    }
  }
}

typeWrite(message2, 200)
  .catch(console.error);

function wait(time) {
  return new Promise(res => setTimeout(res, time));
}  
 <span id="message2">
  Hello 
  <span class="emote">
     <img src="https://static-cdn.jtvnw.net/emoticons/v1/1251411/1.0"> 
  </span> 
  There
</span>  

Комментарии:

1. Это отлично решило мою проблему! Спасибо за помощь! Я сильно сбился с курса с тем материалом, который я пытался. Единственное изменение, которое я внес, это добавил еще один wait() за пределами if{}, поэтому, если текста нет, эффект машинописи между изображениями все еще сохраняется.