前言


會寫這一篇, 說真的有點無奈, 並非針對痞客邦惡搞來的 (畢竟他提供了一個免費的部落格, 讓我不用花錢就有個人網站, 高興的時候隨時可以發表一下). 寫這一篇純粹只是想要記錄及分享如何克服痞客邦網站上一些不是那麼恰當的設定而已.

本文


事情是這樣子: 我寫了一篇有關 UART 接收同步機制及鮑率誤差計算 的貼文, 文章中有些地方用到了分數 (幾分之幾), 剛開始就很直覺的只用 '除號' 來表示 (如: 7/16), 也沒想過到底好不好? 有什麼優缺點? 反正也不會有人有意見.

後來在一個偶然的機會下找到了一個專門用來展現 (render) 數學式的 javascript library: MathJax, 想說就拿上面那一篇來小試一下, 看看效果如何? 果然在 PC 版本一切如預期效果不錯, 分子分母上下分開一目了然. 當下以為一切搞定, 很放心的就 "儲存文章" 把修改的部份發表了出去.

等到發現手機版本有問題已經是二個月之後了. 於是緊急加註了通知, 並且 debug 了一下, 發現又是 星號 '*' 惹事, 這一次是受到手機版模式 "字體放大縮小" 功能的影響. 原因在於 "手機版模式" 有下列三組 CSS 設定有問題:

.article-content-inner.zoom1 * {
    font-size:15px !important;
    line-height:20px !important;
    overflow:hidden visible
}
.article-content-inner.zoom2 * {
    font-size:18px !important;
    line-height:23px !important
}
.article-content-inner.zoom3 * {
    font-size:22px !important;
    line-height:26px !important
}

當中有問題的就是行高的設定 (line-height). 測試時我把行高設定暫時取消就可以看到 "手機版模式" 正確的展現出數學的分數, 可是 CSS 設定並沒有可以取消的功能. 原本以為提高行高的數值或許可以暫時解決問題, 於是有了以下的設定 (MathJax 會插入一些 <span> tag 並且指定 class 是 "mjx-" 開頭的字串, 所以我把它們的高度都一併調高了)

.article-content-inner.zoom1 span[class^="mjx-"],
.article-content-inner.zoom2 span[class^="mjx-"],
.article-content-inner.zoom3 span[class^="mjx-"]  {
    line-height:1.5em !important
}

結果卻是不如預期: 行高是拉高了, 分子分母也分開沒有 "和" 在一起了, 但是卻分子分母位置相反.

寫信給痞客邦客服嗎? 想當然的, 你不會得正面而且滿意的答案 (不騙你, 不信可以自己試試). 客服給我的回覆是 (已縮刪):

  1. 目前手機版模式不支援 CSS 語法的設定及顯示。(只能暫時先用官方的設定. 這個我知道, 因為以前就發生過一次.)
  2. 建議以圖片的方式進行插入放置,或請您可建議瀏覽者於頁面下方切換為「電腦版模式」進行瀏覽.
  3. 已為您整理為建議,並交由站內功能規劃人員進行研議.

即然痞客邦 "手機版模式" 的 CSS 和 "電腦版模式" 不一樣, 而且是固定的, 也沒有可以讓我們一般用戶修改的地方. 所以只好死馬當活馬醫: 不如用 javascript 寫段小程式, 把那個多出來的 line-height 設定刪除.

終於花了幾天辛苦的 google 和測試, 幾次想放棄, 隔天又不死心回頭繼續除錯, 總算是大功告成.

程式及解說


var adjStyleRule = function(sheetname, selector, rule) {
    // Is browser too old?
    if (!document.styleSheets) return [];
    var cRules = (function(sheetName, selector, rule) {
        if (sheetName.length == 0) {
            var cmpFunc = function(i, sheetname) {
                return !document.styleSheets[i].href;
            };
        } else {
            var cmpFunc = function(i, sheetname) {
                return ( document.styleSheets[i].href && 
                         document.styleSheets[i].href.indexOf(sheetname)>=0 );
            }
        }
        var ret = [];
        var rexS = new RegExp(selector, "g");
        var rexR = new RegExp(rule.id+"\\s*:\\s*.*?"+rule.text+";?", "g");
        for (var i = document.styleSheets.length-1; i>=0; i--)
            if (cmpFunc(i, sheetname)) {
                var rules = document.styleSheets[i].cssRules;
                for (var j = rules.length-1; j>=0; j--)
                    if (rules[j].selectorText && rules[j].selectorText.match(rexS)) {
                        var obj = {};
                        obj.ridx = j;
                        obj.selText = rules[j].selectorText;
                        obj.cssText = rules[j].cssText.replace(/^.*\{\s*(.*)\s*\}/, "$1");
                        obj.newtext = obj.cssText.replace(rexR, "");
                        obj.sheet = rules[j].parentStyleSheet;
                        ret.push( obj );
                    }
            }
        return ret;
    })(sheetname, selector, rule);

    if (cRules.length > 0) {
        var i;
        if (cRules[0].sheet.insertRule) {
            for (i=0; i<cRules.length; i++)
                cRules[i].sheet.deleteRule(cRules[i].ridx);
            for (i-- ; i>=0; i--)
                cRules[i].sheet.insertRule(cRules[i].selText + ' { ' + cRules[i].newtext + ' }', cRules[i].ridx);
        } else if (cRules[0].sheet.addRule) {
            for (i=0; i<cRules.length; i++)
                cRules[i].sheet.removeRule(cRules[i].ridx);
            for (i-- ; i>=0; i--)
                cRules[i].sheet.addRule(cRules[i].selText, cRules[i].newtext);
        }
    }
}("", "\\.article-content-inner\\.zoom[1-3] \\*", { id:"line-height", text:"!important" } );

在解說之前, 有一點你要先了解的是:

  1. DOM 架構中的 styleSheets 物件內的 CSS 設定是沒有辦法直接修改的: 即沒有 modify() 操作, 就連直接修改內容 (cssRules[i].cssText) 也是一樣不 work, 唯一可行的方法是 '刪除' 再 '新增'.
  2. 舊版 IE 的 styleSheets 物件雖然同樣有支援 '刪除' 及 '新增' 二種操作, 但是名稱和其他的 browser 不同, 參數也不大一樣.

這支程式的前半段 (第3~33行) 主架構是借用自這一篇貼文, 主要功能是在全部的 styleSheets 物件中找到特定 CSS 設定, 並進行處理. 而我們要找的就是下列這三個 CSS 設定:

  1. .article-content-inner.zoom1 *
  2. .article-content-inner.zoom2 *
  3. .article-content-inner.zoom3 *

為了使程式簡短一些, 我把他們合併成一條 regular expression:
/.article-content-inner.zoom[1-3] */
但是 '.' 及 '*' 這二個字元在 regular expression 中有特殊意義, 所以這二個字元前面要多加一個 '\' 來脫離其特殊意義, 於是就變成了:
/\.article-content-inner\.zoom[1-3] \*/
然後我想要改用字串的方式傳給函數, 然後在函數中建立 regexp 物件, 而不是直接傳 regexp 物件進去, 因此就變成了:
"\\.article-content-inner\\.zoom[1-3] \\*"
(開頭及結尾的 '/' 以 '"' 換掉, 然後以 '\\' 換掉 '\')

找到這三筆 CSS 的同時, 順便把後半段 (第35~48行) 增刪 CSS 設定要用的 selText (原先的 selectorText) 及 newtext (原先的 cssText 去掉 line-height 設定) 處理好.

  • 第26行: 用 regular expression 把 CSS 設定裡大括號 '{', '}' 中的設定提取出來.
  • 第27行: 則針對第26行的結果再進行加工, 刪掉我們不要的 line-height 設定, 並存下來給新增 CSS 設定時使用.

最後 (第35~48行) 把舊的 CSS 設定刪除, 再加入新的 CSS 設定. 不過我用了一點小技巧: 搜尋 CSS 設定時 cssRules 陣列的索引值是由大到小, 找到以後放進結果陣列 (所以 cssRules 序號在結果陣列是倒者排列), 然後要刪除時結果陣列的索引值是由小到大, 所以 cssRules 序號大的先刪除 (所以不會因為刪除了一個 cssRules 之後, 後面的序號要調整). 最後又再由結果陣列的索引值由大到小加回去 cssRules (cssRules 序號小的先加回去). 完成之後 cssRules 的序號會和原本的一模一樣, 只有 line-height 的設定被刪掉了.

用法


如果你也想要運用上面的程式碼, 請把他們拷貝下來, 以純文字模式存成 .js 檔, 並上傳至公用的網頁空間, 例如: Dropbox, Google Drive 或者是 Microsoft OneDrive (詳細的作法可以參考我寫的這一篇部落格利用雲端硬碟作為存放網頁的空間). 然後取得其下載連結. 再修改你要運用的文章加入下載執行該 .js 檔.

如果你用的是 dropbox 記得在後面加上 ?dl=1 來表示是要直接下載檔案, 下面就是我自己使用的例子:

<script type="text/javascript" src="https://www.dropbox.com/s/zx9h2sr969z3nxb/adjStyle.js?dl=1"></script>

如果你用的是 Google Drive, 則改成用下面的格式:
https://docs.google.com/uc?id=[FILE_ID]&export=download

格式中的[FILE_ID]是一連串的 Base64 編碼的字元字串, 可透過 'Share...' 或者 'Get Shareable Link' 功能找到. 例子如下:

<script type="text/javascript" src="https://docs.google.com/uc?id=0B7CTAZbtxbrXNFJCMXVqMktKS0E&export=download"></script>

用 Microsoft OneDrive 也可以, 請改用下面的格式:
https://onedrive.live.com/download?cid=[CID]&resid=[RES_ID]&authkey=[AUTH_KEY]"

格式中的[CID], [RES_ID], [AUTH_KEY]是一連串的 Base64 編碼的字元字串, 其中 [CID] 是你個人相關資訊, [RES_ID], [AUTH_KEY]則是和分享的檔案有關 (每個檔案都不相同). 這些資訊可以用 'Embeded' 功能取得. 例子如下:

<script type="text/javascript" src="https://onedrive.live.com/download?cid=25DF31F5B07B0B51&resid=25DF31F5B07B0B51%211002&authkey=AJJECMWebtTMeU8"></script>

後記及網路安全注意事項


聰明如你, 可能已經也想到: 運用這一段程式或許可以用來刪除痞客邦強制廣告出現的設定 (這個就有點惡搞痞客邦了). 過些時候完成測試後再和各位分享.

註: 其實不建議各位也這樣子從別的網頁空間下載執行 javascript, 這樣子作其實是很危險的, 試想如果不小心網頁空間被駭客給攻佔了, 而該 javascript 被駭客修改去下載/執行不當的程式?

    MagicJackTing 發表在 痞客邦 留言(0) 人氣()