自己实现HTML-Beautify
文章截图 - 更好的排版
源代码下载
学习HTML-Beautify.js之后,我们发现使用JavaScript对HTML进行解析也并不神秘, 首先是逐字符进行分析,从中提取标记(Token),在HTML只存在两种类型的标记-标签和正文, 然后对这些Token进行语法分析,主要是缩进量是多少。
在有这些基本概念后,今天我们就自己来实现这个小程序:
源代码下载
学习HTML-Beautify.js之后,我们发现使用JavaScript对HTML进行解析也并不神秘, 首先是逐字符进行分析,从中提取标记(Token),在HTML只存在两种类型的标记-标签和正文, 然后对这些Token进行语法分析,主要是缩进量是多少。
在有这些基本概念后,今天我们就自己来实现这个小程序:
// 我在Html Beautifier的一个简单尝试
function HtmlBeautify(source, indent_value) {
this.source = source;
this.indent_value = indent_value;
this.result = "";
this.parse();
}
// 分析并产生输出到this.result
HtmlBeautify.prototype.parse = function() {
var that = this;
// 当前分析到哪个字符,当前标记值,标记类型,输出数组,缩进级别
var pos = 0, token_value = "", token_type = "",
output = [], indent_level = 0;
// 把这些标签作为Single Tag
var single_token = "br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed".split(',');
// 输出当前标记(换行+缩进量+标记值)
function outputToken() {
output.push("\n");
for (var i = 0; i < indent_level; i++) {
output.push(that.indent_value);
}
output.push(token_value);
}
// 获取下一个标记(首先获取正文,如果正文为空则获取标签)
function nextToken() {
var token_value_array = [], val = "";
// "<"之前的所有内容作为正文标签
while ((val = that.source[pos]) !== "<") {
if (pos >= that.source.length) {
token_type = "END";
return;
}
token_value_array.push(val);
pos++;
}
token_value = $.trim(token_value_array.join(""));
if (token_value === "") {
// 如果正文标记为空,则获取标签标记
nextTokenTag();
} else {
token_type = "CONTENT";
}
}
function nextTokenTag() {
var token_value_array = [], val = "", tagName = "";
// 获取标签标记,直到遇到">"
do {
val = that.source[pos];
token_value_array.push(val);
pos++;
} while (val !== ">");
token_value = $.trim(token_value_array.join(""));
// 当前标签的名称(小写)
tagName = getTagName();
if (token_value[1] === "/") {
// token_value以"</"开始,则认为是结束标签
token_type = "END_TAG";
} else if (contains(tagName, single_token) || token_value[token_value.length - 2] === "/") {
// 如果标签在single_token或者token_value以"/>"结尾,则认为是独立标签
// 这种判断没有考虑这种情况:"<br></br>"
token_type = "SINGLE_TAG";
} else {
token_type = "START_TAG";
}
}
function getTagName() {
var tagName = token_value.substr(1, token_value.length - 2);
var spaceIndex = tagName.indexOf(" ");
if (spaceIndex > 0) {
tagName = tagName.substr(0, spaceIndex);
}
return tagName.toLowerCase();
}
function contains(val, array) {
for (var i = 0; i < array.length; i++) {
if (val === array[i]) {
return true;
}
}
return false;
}
// parse的主体函数,循环获取下一个Token
while (true) {
nextToken();
// 当前Token为结束标记
if (token_type === "END") {
break;
}
switch (token_type) {
case "START_TAG":
// 我们对缩进的控制非常简单,开始标签后缩进一个单位
outputToken();
indent_level++;
break;
case "END_TAG":
// 结束标签前减少一个单位缩进
indent_level--;
outputToken();
break;
case "SINGLE_TAG":
case "CONTENT":
outputToken();
break;
}
}
// 去除最前面的"\n"
this.result = output.join("").substr(1);
};
$(function() {
$("#format").click(function() {
// 实例化HtmlBeautify,传递需要解析的HTML片段和缩进字符串
var beautify = new HtmlBeautify($("#content").val(), " ");
$("#content").val(beautify.result);
});
});

浙公网安备 33010602011771号