专业汉语词典知识平台,分享汉字词语知识、历史文学知识解答!

励北网
励北网

innerhtml的用法,JS实现innerhtml功能

来源:小易整编  作者:小易  发布时间:2023-03-04 07:24
摘要:innerhtml的用法,JS实现innerhtml功能都知道浏览器和服务端是通过HTTP协议进行数据传输的,而HTTP协议又是纯文本协议,那么浏览器在得到服务端传输过来的HTML字符串,是如何解析成真实的DOM元素的呢,也就是我们常说的生...

innerhtml的用法,JS实现innerhtml功能

都知道浏览器和服务端是通过 HTTP 协议进行数据传输的,而 HTTP 协议又是纯文本协议,那么浏览器在得到服务端传输过来的 HTML 字符串,是如何解析成真实的 DOM 元素的呢,也就是我们常说的生成 DOM Tree,最近了解到状态机这样一个概念,于是就萌生一个想法,实现一个 innerHTML 功能的函数,也算是小小的实践一下。

函数原型

我们实现一个如下的函数,参数是 DOM 元素和 HTML 字符串,将 HTML 字符串转换成真实的 DOM 元素且 append 在参数一传入的 DOM 元素中。

function html(element, htmlString) { // 1. 词法分析 // 2. 语法分析 // 3. 解释执行}复制代码

在上面的注释我已经注明,这个步骤我们分成三个部分,分别是词法分析、语法分析和解释执行。

词法分析

词法分析是特别重要且核心的一部分,具体任务就是:把字符流变成 token 流。

词法分析通常有两种方案,一种是状态机,一种是正则表达式,它们是等效的,选择你喜欢的就好。我们这里选择状态机。

首先我们需要确定 token 种类,我们这里不考虑太复杂的情况,因为我们只对原理进行学习,不可能像浏览器那样有强大的容错能力。除了不考虑容错之外,对于自闭合节点、注释、CDATA 节点暂时均不做考虑。

接下来步入主题,假设我们有如下节点信息,我们会分出哪些 token 来呢。

测试元素

复制代码

对于上述节点信息,我们可以拆分出如下 token

  • 开始标签:<p

  • 属性标签:

  • 文本节点:测试元素

  • 结束标签:

状态机的原理,将整个 HTML 字符串进行遍历,每次读取一个字符,都要进行一次决策(下一个字符处于哪个状态),而且这个决策是和当前状态有关的,这样一来,读取的过程就会得到一个又一个完整的 token,记录到我们最终需要的 tokens 中。

万事开头难,我们首先要确定起初可能处于哪种状态,也就是确定一个 start 函数,在这之前,对词法分析类进行简单的封装,具体如下

function HTMLLexicalParser(htmlString, tokenHandler) { this.token = []; this.tokens = []; this.htmlString = htmlString this.tokenHandler = tokenHandler}复制代码

简单解释下上面的每个属性

  • token:token 的每个字符

  • tokens:存储一个个已经得到的 token

  • htmlString:待处理字符串

  • tokenHandler:token 处理函数,我们每得到一个 token 时,就已经可以进行流式解析

我们可以很容易的知道,字符串要么以普通文本开头,要么以<开头,因此 start 代码如下

HTMLLexicalParser.prototype.start = function(c) { if(c === '<') { this.token.push(c) return this.tagState } else { return this.textState(c) }}复制代码

start处理的比较简单,如果是<字符,表示开始标签或结束标签,因此我们需要下一个字符信息才能确定到底是哪一类 token,所以返回tagState函数去进行再判断,否则我们就认为是文本节点,返回文本状态函数。

接下来分别展开tagState和textState函数。tagState根据下一个字符,判断进入开始标签状态还是结束标签状态,如果是/表示是结束标签,否则是开始标签,textState用来处理每一个文本节点字符,遇到<表示得到一个完整的文本节点 token,代码如下

HTMLLexicalParser.prototype.tagState = function(c) { this.token.push(c) if(c === '/') { return this.endTagState } else { return this.startTagState }}HTMLLexicalParser.prototype.textState = function(c) { if(c === '<') { this.emitToken('text', this.token.join('')) this.token = [] return this.start(c) } else { this.token.push(c) return this.textState }}复制代码

这里初次见面的函数是emitToken、startTagState和endTagState。

emitToken用来将产生的完整 token 存储在 tokens 中,参数是 token 类型和值。

startTagState用来处理开始标签,这里有三种情形

  • 如果接下来的字符是字母,则认定依旧处于开始标签态

  • 遇到空格,则认定开始标签态结束,接下来是处理属性了

  • 遇到>,同样认定为开始标签态结束,但接下来是处理新的节点信息

endTagState用来处理结束标签,结束标签不存在属性,因此只有两种情形

  • 如果接下来的字符是字母,则认定依旧处于结束标签态

  • 遇到>,同样认定为结束标签态结束,但接下来是处理新的节点信息

逻辑上面说的比较清楚了,代码也比较简单,看看就好啦

HTMLLexicalParser.prototype.emitToken = function(type, value) { var res = { type, value } this.tokens.push(res) // 流式处理 this.tokenHandler && this.tokenHandler(res)}HTMLLexicalParser.prototype.startTagState = function(c) { if(c.match(/[a-zA-Z]/)) { this.token.push(c.toLowerCase()) return this.startTagState } if(c === ' ') { this.emitToken('startTag', this.token.join('')) this.token = [] return this.attrState } if(c === '>') { this.emitToken('startTag', this.token.join('')) this.token = [] return this.start }}HTMLLexicalParser.prototype.endTagState = function(c) { if(c.match(/[a-zA-Z]/)) { this.token.push(c.toLowerCase()) return this.endTagState } if(c === '>') { this.token.push(c) this.emitToken('endTag', this.token.join('')) this.token = [] return this.start }}复制代码

最后只有属性标签需要处理了,也就是上面看到的attrState函数,也处理三种情形

  • 如果是字母、单引号、双引号、等号,则认定为依旧处于属性标签态

  • 如果遇到空格,则表示属性标签态结束,接下来进入新的属性标签态

  • 如果遇到>,则认定为属性标签态结束,接下来开始新的节点信息

代码如下

HTMLLexicalParser.prototype.attrState = function(c) { if(c.match(/[a-zA-Z'"=]/)) { this.token.push(c) return this.attrState } if(c === ' ') { this.emitToken('attr', this.token.join('')) this.token = [] return this.attrState } if(c === '>') { this.emitToken('attr', this.token.join('')) this.token = [] return this.start }}复制代码

最后我们提供一个parse解析函数,和可能用到的getOutPut函数来获取结果即可,就不啰嗦了,上代码

HTMLLexicalParser.prototype.parse = function() { var state = this.start; for(var c of this.htmlString.split('')) { state = state.bind(this)(c) }}HTMLLexicalParser.prototype.getOutPut = function() { return this.tokens}复制代码

接下来简单测试一下,对于

测试并列元素的

测试并列元素的

HTML 字符串,输出结果为

innerhtml的用法,JS实现innerhtml功能

看上去结果很 nice,接下来进入语法分析步骤

语法分析


本文地址:百科问答频道 https://www.neebe.cn/wenda/916276.html,易企推百科一个免费的知识分享平台,本站部分文章来网络分享,本着互联网分享的精神,如有涉及到您的权益,请联系我们删除,谢谢!


百科问答
小编:小易整编
相关文章相关阅读
  • JS 字符串转数组

    JS 字符串转数组

    JS中,将一个字符串转置为数组,使用到的方法是split(),通过使用split()方法,可以轻松的将一个字符串转换为数组操作方法01新建一个HTML文档,用于承载JS02...

  • 手机蓝牙的作用和用法(手机蓝牙有什么用途)

    手机蓝牙的作用和用法(手机蓝牙有什么用途)

    每部手机上都有一个蓝牙功能,大家以前都会用它来传输文件,不过随着移动网络和WiFi的普及,这个功能也渐渐被大家忽略了。今天,我们就来科普一些手机蓝牙不为人知的功能,让大家可以更好的使用这一功能。1、传输文件传输文件,这一功能应该是大家最熟悉...

  • 磁盘阵列raid5的用法

    磁盘阵列raid5的用法

    操作方法01在日常应用中,大多是把服务器上所有的硬盘创建RAID5,并且只划分了一个“逻辑磁盘”,这样从理论上来讲没有任何问题,在实际中也可以可以使用的,但是这种方法并不可取。RAID5是一种存储性...

  • C语言while语句的用法

    C语言while语句的用法

    while语句的一般形式为:while(表达式)语句其中表达式是循环条件,语句为循环体。#includeintmain(void){inti,sum=0;i=1;while(i...

  • 详解Linux中hdparm命令查看硬盘信息的用法

    详解Linux中hdparm命令查看硬盘信息的用法

    功能说明:显示与设定硬盘的参数。语  法:hdparm[-CfghiIqtTvyYZ][-a][-A][-c][-d][-k][-K][-m][-n...

  • doT.js是什么

    doT.js是什么

    doT.js是一个JavaScript模板框架,在web前端使用dot.js作为模板引擎,主要的用途就是,在写好的模板上,放进数据,生成含有数据的html代码。doT.js是一个JavaScript模板框架,在web前端使用do...

  • JsRender是什么

    JsRender是什么

    JsRender是jQuery模板,专为高性能的纯字符串渲染而优化,无需DOM和其他jQuery依赖。允许定义一次样板结构并重复使用它来动态生成HTML。JsRender是jQuery模板,专为高性能的纯字符串渲染而优...

  • 机动都市阿尔法枪盾好用吗 枪盾用法大全

    机动都市阿尔法枪盾好用吗 枪盾用法大全

    机动都市阿尔法中各式各样的武器,每个都有自己的特点,枪盾也是深受玩家青睐,那么机动都市阿尔法枪盾好用吗,下面就和小编一起来了解一下吧。一、武器简介:枪盾从外观即可看出组成,一面盾牌,一把散弹枪,可以看出这是攻守兼备的一个武器,尤其是在近战中...

  • 周排行
  • 月排行
  • 年排行

精彩推荐