【问题标题】:Type Writter effect with HTML tags inside the text文本内带有 HTML 标记的打字机效果
【发布时间】:2019-08-19 12:45:28
【问题描述】:

我正在尝试实现我所做的打字效果,但问题是我拥有和正在输入的文本在 <p> 标签内,因为它的内部 HTML 也有一些 HTML 标签,如 <span></span> 并且因为打字是char by char 它被输入是<span> 而不是呈现为元素。有什么方法可以在保持 HTML 标签不变的同时达到效果吗?我突出显示了包含在 span 中的单词,我最终想要实现的是将输入的单词突出显示...

  function typeWriter() {
    if (i < txt.length) {
      document.getElementById("content_html").innerHTML += txt.charAt(i);
      i++;
      setTimeout(typeWriter, self.typing.speed);
    }
  }
  typeWriter();

这是我尝试输入的文本示例:

<p>Surface chemistry deals with phenomena that occur at the surfaces or interfaces. Many important phenomena, noticeable amongst this being corrosion, electrode processes,&nbsp;heterogeneous catalysis, dissolution, and crystallization occur at interfaces. The subject of surface chemistry finds many applications in industry, analytical work, and daily life situations.</p>

它包含一个&lt;p&gt; 标记,将来可以更改为任何其他 HTML 标记,因为它来自服务器...

【问题讨论】:

  • 所以澄清一下,您希望&lt;p&gt; 标记内的文本具有类型编写器效果。您希望 &lt;p&gt; 标记内的任何单词周围都有一个锚标记包装器来显示相同​​的类型编写器效果 - 但不是剥离其锚属性的元素,它将保留它们,并且一旦类型编写器效果完成,您就可以如您所愿点击链接?
  • 不,我有一个父

    标签,它应该包含来自服务器的任何文本,但是来自服务器的文本可以包含 HTML 标签本身,就像用 spans 包裹的单词以获得某些效果......跨度>

  • 如何使用库来解决这个问题。 jsfiddle.net/we8tchjk
  • @little_coder 该库做得对,但我有一个错误,它永远不会停止一遍又一遍地输入文本,必须在其脚本文件中查找错误
  • @little_coder 该库完美地完成了这项工作!错误就在我这边,我使用的是 Vue.js,由于动态内容渲染,它确实在循环中运行。这是在实现效果的同时保留样式、内部标签和动态内容的最佳选择,请将其添加为答案,以便我接受它

标签: javascript html css


【解决方案1】:

我不完全理解您的问题,但您也可以使用纯 CSS 实现打字动画,如 this Codepen 所示,这可以解决您遇到的一些问题。

animation: typing 7s steps(15, end), /* # of steps = # of chars */
               blink-caret .5s step-end infinite alternate;

【讨论】:

  • 这是一个很好的解决方案,但不是动态的。您必须计算不包括 html 标记的字符串的长度,然后使用 javascript 注入或级联 css。
【解决方案2】:

我相信你正在寻找 document.getElementById("demo").innerText = "&lt;p&gt;asd&lt;/p&gt;";

而不是innerHTML

这样它将精确呈现"&lt;p&gt;asd&lt;/p&gt;",而不是创建一个元素

【讨论】:

  • 确实如此,但是当您逐个字符渲染时,引擎不理解打开的

    标签并将其视为文本

  • 哦,所以我没有完全理解你。所以你想显示&lt;p,但是当它恰好是完整的html时,它应该呈现html而不是?
  • 好的,我想我明白了。首先,您必须从服务器解析文本,创建元素,然后将文本放入其中...
【解决方案3】:

在您将所有内容添加到其中后,试试我在哪里渲染整个文本。

<script>
        var i = 0;
        txt = "<p>test this out</p>";
        function typeWriter() {
            if (i < txt.length) {
                document.getElementById("content_html").innerHTML += txt.charAt(i);
                i++;
                setTimeout(typeWriter, 100);
            } else {
                document.getElementById("content_html").innerHTML = document.getElementById("content_html").innerText;
            }
        }
        typeWriter();
    </script>

【讨论】:

    【解决方案4】:

    所以我想自己做,因为任务很有趣。我知道@little_coder 已经在 cmets 中提供了最佳答案。

    这就是我所做的:

    js:

    // text
    txt = "asd<p>test this out<span>with more </span>and between text<span class='second'>second</span></p>afe";
    
    //shared array
    var instructions = [] ;
    
    
    // typeWriter
    var i = 0; // 
    var j = 0;
    var elem = '';
    var elem_value = '';
    var speed = 50;
    
    function typeWriter() {
    
      if(j < instructions.length){
        if (typeof instructions[j][1] == 'string'){
          if (i < txt.length) {
            instructions[j][0].innerHTML += instructions[j][1].charAt(i);
            i++;
            setTimeout(typeWriter, speed);
          }else{
            j=j+1;
            i = 0;
            setTimeout(typeWriter, speed);
          }
        }
        else if(typeof instructions[j][1] == 'object'){
            console.log("ins", instructions[j][0]);
          instructions[j][0].appendChild(instructions[j][1]);
          j=j+1;
          i=0;
          typeWriter();
        }
      }
    }
    //  
    
    
    // recreateNode
    parser = new DOMParser();
    
    function recreateNode(list, container){
      doc = parser.parseFromString(list, "text/html");
      doc.body.childNodes.forEach(function(a){
            console.log(a);
        if(a.nodeName == '#text'){
          instructions.push([container, a.nodeValue])     
        }
        else{ // if there is element to create
          b = a.cloneNode(true); // handle deep elements
          c = a.cloneNode(false); // this way I can get ONLY the element with attributes and classes
    
          /* container.appendChild(c) */; // I append only element
          instructions.push([container, c]);
            recreateNode(b.innerHTML, c); // b will be appended to c
    
        }
      });
    
    }
    
    // init
    
    parent = document.getElementById("content_html")
    recreateNode(txt, parent);
    typeWriter();
    

    首先我创建了recreateNode,它创建了instructions 数组,该数组存储了用于重新创建text 的html 结构的步骤。在typeWriter 中使用此说明,我制作typing effectcreate html element。遵守指令container 值我知道在哪里放置下一个文本。

    一切都会好起来的,但是当涉及到.appendChild 时,或者至少我认为这是原因......附加元素需要时间,这使得typing effect 不流畅。

    js 小提琴:https://jsfiddle.net/an5gzL7f/3/

    提前感谢您提供如何快速运行的任何提示

    【讨论】:

      【解决方案5】:

      我刚刚在更新 TypeIt (https://typeitjs.com) 时遇到了类似的挑战,以便它可以处理键入嵌套的 HTML。简而言之,我使用的方法是反复遍历父元素的所有childNodes,拉出每个嵌套节点并将其展开为队列项数组以供稍后键入。

      完成这一切有点麻烦,但如果它有帮助,欢迎您搜索代码以了解我是如何做到的:

      https://github.com/alexmacarthur/typeit/blob/master/src/helpers/chunkStrings.js#L98

      其中很多都是 TypeIt 特有的——尤其是我如何构造对象,其属性包含重构这些元素所需的信息。

      【讨论】:

        【解决方案6】:

        最优雅的解决方案可能是仅使用 CSS 的解决方案。但是它也有不利的一面,如果文本有换行符或者不是以等宽字体编写的,这基本上是不切实际的。我冒昧地编写了一个代码补丁来配合其余的解决方案,这些解决方案都以自己的方式有用。

        我的解决方案的缺点是我使用了textContent 属性,它不会呈现为 HTML。出于安全原因,我拒绝使用 innerHTML。所以现在添加&lt;a&gt;标签可能不是最好的主意。

        我的方法的目标是使用window.requestAnimationFrame 钩子,因为它非常强大。其余的代码仍然有点混乱,但至少它是不言自明的。

        let paragraphNode = document.querySelector('p');
        let textObject = {
          actionCounter: 0,
          typeDelay: 6,
          cursorDelay: 22,
          cursorShow: false,
          currentIndex: 0,
          stringLength: paragraphNode.textContent.length,
          textContent: paragraphNode.textContent
        }
        paragraphNode.textContent = "";
        
        const writeText = ()=>{
          textObject.actionCounter ++;
          if( textObject.actionCounter % textObject.typeDelay == 0 ){
            textObject.currentIndex += 1;
          }
          let string = textObject.textContent.substring(0, textObject.currentIndex);
          if(  textObject.actionCounter % textObject.cursorDelay == 0 ){
            textObject.cursorShow = !textObject.cursorShow;
          }
          paragraphNode.textContent = (textObject.cursorShow) ? string + "|" : string;
          window.requestAnimationFrame(writeText);
        }
        
        window.requestAnimationFrame(writeText);
        

        这是我制定的 codepen 示例。在撰写本文时,唯一缺少的是一个足够勇敢的人将textContent 替换为innerHTML,并可能检查下一个字母是否为“

        typewriter on codepen

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-08
          相关资源
          最近更新 更多