读取html字符串,转为tokens数组。
scanner.js
/**
* 扫描器类
*/
class Scanner {
/**
* 构造器
* @param str html字符串
*/
constructor(str) {
this.str = str;
this.pos = 0;
this.tokens = [];
this.scan();
}
/**
* 扫描
*/
scan() {
while(this.pos < this.str.length) {
if(this.readText()) {
this.readPoint();
this.readTag();
this.readBlank();
while(this.str[this.pos] !== '>' && this.str[this.pos] !== '/') {
this.readAttribute();
this.readBlank();
}
this.readPoint();
}else {
break;
}
}
}
/**
* 读取纯文本
* @returns {boolean}
*/
readText() {
let text = '';
while(this.str[this.pos] !== '<' && this.pos < this.str.length) {
text += this.str[this.pos];
this.pos ++;
}
if(text) {
this.tokens.push(['text',text]);
}
return this.pos < this.str.length;
}
/**
* 读取尖括号
*/
readPoint() {
if(this.str[this.pos] === '/') {
this.pos += 2;
}else {
this.pos ++;
}
}
/**
* 读取标签名
*/
readTag() {
let tag = '';
while(!/\s/.test(this.str[this.pos]) && '>' !== this.str[this.pos]) {
tag += this.str[this.pos];
this.pos ++;
}
if(tag.startsWith("/")) {
this.tokens.push(['endTag',tag]);
}else {
this.tokens.push(['startTag',tag]);
}
}
/**
* 读取空白符
*/
readBlank() {
while(/\s/.test(this.str[this.pos])) {
this.pos ++;
}
}
/**
* 读取属性
*/
readAttribute() {
let attribute = '';
while(!/\s/.test(this.str[this.pos]) && this.str[this.pos] !== '>') {
if(this.str[this.pos] === '=') {
this.readAttributePair(attribute);
return;
}
attribute += this.str[this.pos];
this.pos ++;
}
this.tokens.push(['attribute',attribute]);
}
/**
* 读取成对的属性
* @param key
*/
readAttributePair(key) {
let value = '';
this.pos += 2;
while(this.str[this.pos] !== '\"') {
value += this.str[this.pos];
this.pos ++;
}
this.tokens.push([key,value]);
this.pos ++;
}
/**
* 输出所有的tokens
*/
showTokens() {
console.log(this.tokens);
}
}
index.js
let str = `<div id="app">
<h1 style="color: green;">123456</h1>
<h1 style="color: gold;">Hello</h1>
<img src="aaa" alt="bbb">
<input type="button" value="测试" id="btn" readonly >
</div>
`;
let scanner = new Scanner(str);
scanner.showTokens();
输出结果(node.js)
[ [ 'startTag', 'div' ],
[ 'id', 'app' ],
[ 'text', '\n' ],
[ 'startTag', 'h1' ],
[ 'style', 'color: green;' ],
[ 'text', '123456' ],
[ 'endTag', '/h1' ],
[ 'text', '\n ' ],
[ 'startTag', 'h1' ],
[ 'style', 'color: gold;' ],
[ 'text', 'Hello' ],
[ 'endTag', '/h1' ],
[ 'text', '\n ' ],
[ 'startTag', 'img' ],
[ 'src', 'aaa' ],
[ 'alt', 'bbb' ],
[ 'text', '\n ' ],
[ 'startTag', 'input' ],
[ 'type', 'button' ],
[ 'value', '测试' ],
[ 'id', 'btn' ],
[ 'attribute', 'readonly' ],
[ 'text', '\n ' ],
[ 'endTag', '/div' ],
[ 'text', '\n ' ] ]