踩到一個地雷。從舊專案搬了網頁到新專案,有些文字顯示不出來,花了點時間追查,發現是因為某 jQuery 元件用了 <span class="..." /> 這種不合法寫法,舊專案為相容 IE 還在用 jQuery 用 1.12 版,搬到改用 jQuery 3.6 的新專案就壞掉了!

XML 語法中 <elementTag ...></elementTag><elementTag ... /> 意義相同且可互換;但依據 HTML5 規範,Self-Closing Tag 只能用在 Void Element (包含 area, base, br, col, embed, hr, img, input, link, meta, source, track, wbr) 以及 Foreign Element (MathML 及 SVG 元素)。因此,<span ... /> 並不是合法寫法,可視為元件程式庫的 Bug。這次遇到出錯案例為 <span class="icon" /><span>xxx</span>,被瀏覽器解讀為 <span class="icon"><span>xxx</span></span>,等同 xxx 被包在 <span class="icon"> 裡,而 .icon 樣式有設定固定寬高及超出範圍隱藏(overflow: hidden),導致包在其中 xxx 無法完整顯示。

但,既然有錯,這麼多年下來為什麼沒爆?是因為該元件靠 jQuery append('<span class="icon" /><span>xxx</span>') 將 HTML 轉成 DOM,在 jQuery 1.X 的執行結果是 <span class="icon"></span><span>xxx</span>,陰錯陽差修正問題。但 jQuery 3.6 行為改變,會保留原有 <span class="icon" /><span>xxx</span> 格式,因而東窗事發。

找出問題根源,也確認 HTML5 對自閉標籤的規定,用下面的範例為本茶包劃上完美句點。
補充:程式包含一個較少用的技巧,網頁同時載入 jQuery 3.6 及 jQuery 1.12,靠 jQuery.noConflict() 讓二者並存,這個 API 十幾年前就學到,還真沒想過有派上用場的一天,哈。

<!DOCTYPE html>
<html>
<hea>
    <meta charset="utf-8">
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>var $jq36 = jQuery.noConflict();</script>
    <script src="https://code.jquery.com/jquery-1.12.0.min.js"></script>
    <script>var $jq112 = jQuery.noConflict();</script>
    <style>
        .square {
            display: inline-block;
            width: 20px;
            height: 20px;
            background-color: lightseagreen;
            overflow: hidden;
        }

        dd {
            padding: 6px; margin-left: 20px;
        }
    </style>
</hea>

<body>
    <dl>
        <dt>Normal SPAN</dt>
        <dd>
            <span class="square"></span>
            <span>Text</span>
        </dd>
    </dl>
    <dl>
        <dt>Self-Closed SPAN</dt>
        <dd>
            <span class="square" />
            <span>Text</span>   
        </dd>
    </dl>
    <dl>
        <dt>jQuery.append Self-Closed SPAN (v3.6)</dt>
        <dd class="jqtest3">
        </dd>
    </dl>
    <dl>
        <dt>jQuery.append Self-Closed SPAN (v1.12)</dt>
        <dd class="jqtest1">
        </dd>
    </dl>    
    <script>
        $jq36('.jqtest3').append('<span class="square" /><span>Text</span>');
        $jq112('.jqtest1').append('<span class="square" /><span>Text</span>');
    </script>
</body>

</html>

由上到下分別測試:正常 Span、自閉 Sapn、使用 jQuery 3.6 append() 轉換自閉 Span、使用 jQuery 1.12 轉換自閉 Span:

[2022-10-18 更新] 感謝讀者 Nickchen Nick 補充,jQuery 於 3.5 版 修正 jQuery.htmlPrefilter HTML 標籤自動修正功能以防範 XSS 攻擊。透過 jQuery Migrate Plugin 可恢復舊版行為,但需酌斟風險。

A case of using invalid self-closing SPAN in HTML5 browser causes incorrect display.


Comments

Be the first to post a comment

Post a comment