JS實現HTML實體與字符的相互轉換(二)

本系列的前一篇文章講述了HTML實體編碼(10進制、16進制)與字符的相互轉換,本文將講述HTML命名實體與字符的相互轉換,如&lt;轉義成<。你可能想問我爲什麼兩篇文章間隔了約三個月,其實我本來沒想續寫,但是之前因爲沒寫到命名實體,所以總感覺不完美,而且最近又想起了這個問題,於是準備寫個解決方案,來彌補遺憾。好了,準備言歸正傳。PS:如有錯誤,請不吝指正。

1. 字符轉爲html命名實體

針對這個問題,可以分爲兩種情況:一種是隻包含&、<、>、'的html實體,另一種是廣義的實體,不只侷限於上面的情況。對於後者,在我看來,除了列舉出所有的實體符號,寫switch case語句,還真的沒有什麼好辦法。(如果您有什麼好辦法,請不吝賜教。)。針對前者的話,其實原生js就支持。例如會自動對文本中存在的HTML語法字符(小於號、大於號、引號及和號)進行編碼的節點的innerText屬性(FireFox中是textContent屬性。實際上二者並不完全一樣,innerText會忽略行內樣式和腳本,而textContent則會原樣返回行內樣式和文本。)。其原理是設置innerText會生成當前節點的一個子文本節點,而爲了確保只生成一個子文本節點,就需要對文本進行HTML編碼。innerHTML雖然也可以做到,但它轉變的只是標籤的文本。下面的例子展示了它們的不同。

var div=document.createElement('div');
div.innerText='<p>hello & world</p>';
div.innerText //<p>hello & world</p>"
div.innerHTML //"&lt;p&gt;hello &amp; world&lt;/p&gt;"

div.innerHTML='<p>hello & < world</p>'
div.innerHTML //"<p>hello &amp; &lt;  world</p>"
div.innerText //"hello & < world"

從上面例子中可以看到二者的區別:innerText會將所有的文本轉義(當然也不是全部文本,比如空格就不會),innerHTML則是對標籤內的文本進行轉義,標籤如<p>就不會轉義,但孤立的小於大於號還是會進行轉換的。(上面代碼中innerHTML之所以設置的內容和解析後的內容不一樣,是因爲返回的是瀏覽器根據原始字符串解析爲DOM樹後經過序列化之後的結果。)根據上面程序的結果,我們可以得到簡單的轉換函數:

//僅限於包含`&、<、>、'`的文本轉換
function stringToEntity(str){
  var div=document.createElement('div');
  div.innerText=str;
  div.textContent=str;
  var res=div.innerHTML;
  console.log(str,'->',res);
  return res;
}

其實除了innerText,還可以通過創建文本節點的方式來完成轉義,即使用document.createTextNode()。這種方法大部分的應用場景是對用戶輸入進行轉義。例如業務需要,我們需要把用戶的輸入寫到網頁上,不做轉義直接將用戶輸入寫到網頁上往往是行不通的,因爲容易出現XSS漏洞。不過我們可以通過document.createTextNode()方法將用戶輸入作爲文本節點,然後再插入到文檔中。該方法會對出現的特殊標記進行轉義。例如如下代碼:

var str="<img src='a valid url' onload='alert(1)'></img>";
var text=document.createTextNode(str);
$("container").appendChild(text);

上述代碼中如果不加轉義直接使用$("container").innerHTML=str;就會使得圖片加載完運行onload裏面的代碼,如果代碼是惡意的,就會爲我們網站的用戶造成損害。而將小於號、大於號轉義後就不會出現這個問題了。

2. html命名實體轉換爲字符

雖然不能直接寫出字符轉換爲html實體的簡單程序,但是寫出字符轉換爲html實體的程序還是可以的。如以下代碼:

function entityToString(entity){
  var div=document.createElement('div');
  div.innerHTML=entity;
  var res=div.innerText||div.textContent;
  console.log(entity,'->',res);
  return res;
}
//test
entityToString('&lt;hello&nbsp;world&gt;')
//output:  &lt;hello&nbsp;world&gt; -> <hello world>
//output:  "<hello world>"

將輸入的實體符號賦值給div.innerHTML,通過div.innerTextdiv.textContent取出即可得到轉義後的文本。

好了,以上就是我想和大家分享的內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章