自己實現HTML-Beautify
文章截圖 - 更好的排版
原始碼下載
上一篇文章,我們按照 的基本框架,用我們自己的語言實現了對Html簡單的格式化功能。
今天我們來完善這段JavaScript指令碼,主要新增如下功能:
刪除多餘的空格,回車換行以及製表符
保持script和style標籤裡面的內容格式不變化
註釋部分的格式不變化
標籤屬性賦值等號前後不能有空格(type="text/javascript")
自封閉標籤/>之前必須有空格(
)
原始碼:
// 最佳化過的HTML-Beautify
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, is_format_content = true;
// 把這些標籤作為Single Tag
var single_token = "br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed".split(',');
var white_space = "rnt ".split("");
// 獲取下一個標記(首先獲取正文,如果正文為空則獲取標籤)
function nextToken() {
var token_value_array = [], val = "", space = false;
// "<"之前的所有內容作為正文標籤
while ((val = that.source[pos]) !== "<") {
if (pos >= that.source.length) {
token_type = "END";
return;
}
if (is_format_content) {
if ($.inArray(val, white_space) >= 0) {
space = true;
pos++;
continue;
}
if (space) {
token_value_array.push(" ");
space = false;
}
}
token_value_array.push(val);
pos++;
}
token_value = token_value_array.join("").replace(/^n+|n$/g,"");
if ($.trim(token_value) === "") {
// 如果正文標記為空,則獲取標籤標記
if(!is_format_content) {
is_format_content = true;
}
nextTokenTag();
} else {
token_type = "CONTENT";
}
}
// 下一個標籤標記
function nextTokenTag() {
var token_value_array = [], val = "",
tagName = "", space = false, is_comment = false;
// 這是一個註釋標籤
if(that.source[pos + 1] === "!" &&
that.source[pos + 2] === "-" &&
that.source[pos + 3] === "-") {
is_comment = true;
}
// 獲取標籤標記,直到遇到">"
do {
val = that.source[pos];
if (!is_comment) {
// 如果此字元為空格換行製表符,則跳過此字元
if ($.inArray(val, white_space) >= 0) {
space = true;
pos++;
continue;
}
if (space) {
if(token_value_array[token_value_array.length - 1] !== "=" && val !== "=") {
token_value_array.push(" ");
}
space = false;
}
}
if(val === "/" && that.source[pos + 1] === ">" && token_value_array[token_value_array.length - 1] !== " ") {
token_value_array.push(" ");
}
token_value_array.push(val);
pos++;
} while (val !== ">");
token_value = $.trim(token_value_array.join(""));
// 當前標籤的名稱(小寫)
tagName = getTagName();
if(is_comment) {
token_type = "SINGLE_TAG";
} else {
if (token_value[1] === "/") {
// token_value以"</"開始,則認為是結束標籤
token_type = "END_TAG";
} else if ($.inArray(tagName, single_token) >= 0 || token_value[token_value.length - 2] === "/") {
// 如果標籤在single_token或者token_value以"/>"結尾,則認為是獨立標籤
// 這種判斷沒有考慮這種情況:"<br></br>"
token_type = "SINGLE_TAG";
} else {
token_type = "START_TAG";
if (tagName === "script" || tagName === "style") {
is_format_content = false;
}
}
}
}
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 outputToken() {
output.push(token_value);
}
// 輸出新行
function outputLine() {
output.push("n");
}
// 輸出縮排
function outputIndent() {
for (var i = 0; i < indent_level; i++) {
output.push(that.indent_value);
}
}
// parse的主體函式,迴圈獲取下一個Token
while (true) {
nextToken();
// 當前Token為結束標記
if (token_type === "END") {
break;
}
switch (token_type) {
case "START_TAG":
// 我們對縮排的控制非常簡單,開始標籤後縮排一個單位
outputLine();
outputIndent();
outputToken();
indent_level++;
break;
case "END_TAG":
// 結束標籤前減少一個單位縮排
indent_level--;
outputLine();
outputIndent();
outputToken();
break;
case "SINGLE_TAG":
outputLine();
outputIndent();
outputToken();
break;
case "CONTENT":
outputLine();
if(is_format_content) {
outputIndent();
}
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);
});
});
此時的程式碼已經有點複雜了,而對於更加複雜的環境,比如對JavaScript的格式化如果還是這樣手工解析勢必會更加複雜,或許有更好的解決辦法。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2768/viewspace-2806204/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 自己實現AJAX
- 實現自己的promisePromise
- 自己動手實現OkHttpHTTP
- 自己實現鬥地主引擎
- strlen strcat strcpy strcmp 自己實現
- 實現一個自己的mvvmMVVM
- Flume 實現自己的實時日誌(2)
- DIY 實現 ThinkPHP 核心框架(六)實現自己的 Composer 包PHP框架
- 自己實現一個window.location
- 【web前端】自己實現Array.reduce()Web前端
- 如何實現自己的Spring Boot StarterSpring Boot
- 使用Runtime來實現自己的KVO
- 自己實現一個java的arraylistJava
- 自己動手實現一個前端路由前端路由
- 自己實現一個滑動視窗
- 自己實現三個API(getSiblings,addCLass,text)API
- 4.16 實現自己的堆管理器
- 如何實現自己的SpringBoot自動配置Spring Boot
- [翻譯] 使用JavaScript實現自己的PromisesJavaScriptPromise
- 自己動手實現一個EventBus框架框架
- PHP 自己實現var_dump函式PHP函式
- 自己實現一個Controller——終極型Controller
- 自己實現一個Controller——精簡型Controller
- react同構實踐——實現自己的同構模板React
- 自己動手實現 Shell 多程式管道符
- 自己動手實現Java中的StringBuffer類Java
- 如何自己實現一個 mobx – 原理解析
- 自己實現陣列的 `map`、`filter`、`find` 方法陣列Filter
- 基於Netty自己動手實現Web框架NettyWeb框架
- 自己動手實現一個阻塞佇列佇列
- 自己動手實現一個簡單的 IOC
- 實現一個屬於自己的React框架(一)React框架
- go語言實現自己的RPC:go rpc codecGoRPC
- 實現自己的Vue Router -- Vue Router原理解析Vue
- 自己實現一個SAP WebClient UI Repository Information SystemWebclientUIORM
- 自己用 Netty 實現一個簡單的 RPCNettyRPC
- 自己實現一個Electron跨程式訊息元件元件
- 說說instanceof和typeof的實現原理並自己模擬實現一個instanceof