接上篇部落格,現在增加一個新需求:h5頁面B區顯示富文字HTML片段。
功能描述
-
要求
-
有一段PC端顯示的富文字HTML片段,在手機H5頁面B區上載入顯示
-
保持PC端的樣式縮放適應手機螢幕
-
如果HTML富文字有圖片
- 圖片預設不載入
- 當手機可視區間到B區時候,圖片觸發懶載入顯示
- 點選富文字圖時候,有彈層載入圖片,可放大檢視
-
-
提示
- 可以考慮用iframe方式處理富文字
- 但是要考慮到iframe的父子頁面的通訊和相容性
- 還有考慮到iframe裡富文字圖片懶載入導致高度變化帶來的父頁面的高度自適應
具體實現過程
為什麼選擇iframe
看到需求時,第一想到的是隻是顯示一段富文字內容,為什麼要用iframe,直接顯示不就行了。 iframe還是有挺多缺點的:
- 會產生很多頁面,不容易管理
- 不容易列印
- 瀏覽器的後退按鈕無效
- 程式碼複雜,無法被一些搜尋引擎索引到,這一點很關鍵,現在的搜尋引擎爬蟲還不能很好的處理iframe中的內容,所以使用iframe會不利於搜尋引擎優化。
- 多數小型的移動裝置(PDA 手機)無法完全顯示框架,裝置相容性差。
- 多框架的頁面會增加伺服器的http請求,對於大型網站是不可取的。
這麼多缺點為什麼還要用呢(關鍵是我對iframe的使用不太熟悉,手動微笑。。。)
高人指點,因為想用iframe隔離全域性樣式對富文字樣式的影響,emmmm,確實很有道理。
比如你在全域性樣式裡寫了一個樣式:
p {
font-size: 18px;
}
複製程式碼
那麼富文字的內容也會受到這個全域性樣式的影響,所有p裡面的字型大小都會是18px。這可不是我們想得到的。
其實看到這,肯定會想到,只要在全域性樣式裡注意一下,不要寫一些可能會影響全域性的樣式就行了,比如不寫標籤的樣式。這對於一個新專案可能較為容易去實現,如果一個專案已經非常龐大或經過多人的修改,把不規範的樣式全部改掉好像有點不太現實。
好了,暫時沒有想到其他好的辦法,只能選擇iframe。
實現思路
圖片自適應
因為返回的資料是一段html片段,所以選擇iframe的srcdoc屬性(規定在<iframe>中顯示的頁面的 HTML 內容),我們可以在獲取到資料之後對資料進行一些處理(圖片懶載入,後面會講到),然後再字串拼接成一段完整的HTML。
html中設定meta標籤,允許使用者縮放:
<meta name="viewport" content="width=device-width, initial-scale=1">
複製程式碼
設定全域性樣式
<style>
body {height: ${clientHeight}px;}
img {max-width: ${clientWidth}px;}
</style>
複製程式碼
clientHeight是螢幕高度,clientWidth是螢幕寬度。
設定body的高度是因為這篇部落格需求是接著上篇部落格的需求來的(需要實現阻尼效果),在iframe裡,我禁用了scroll,具體思路可參考上篇部落格。
設定img最大寬度是為了實現圖片自適應,不會因為太大而超出螢幕。
圖片懶載入
直接上程式碼:
修改字串內容:
const newHtml = result.content.replace(/<img.*?\/>/g, match => {
return match.replace('src', 'src="./images/iframe/loading.gif" data-src');
});
複製程式碼
懶載入函式:
const clientHeight = $(window).height();
let continueLoading = true;
const imgTags = document.getElementsByTagName('img');
const lazyload = () => {
let num = 0;
for (let img of imgTags) {
const { top } = img.getBoundingClientRect();
if (/loading.gif/.test(img.src)) {
top < clientHeight && (img.src = img.dataset.src);
} else {
num++;
}
}
num === imgTags.length && (continueLoading = false);
};
$('.main').on('touchmove', e => {
continueLoading && lazyload();
});
複製程式碼
我這裡是先用正則進行替換,先顯示loading,並用data-src記住圖片地址。然後再touchmove事件中檢測並進行懶載入:
getBoundingClientRect()用來檢測圖片是否在可視區域內。
continueLoading是一個標誌,當所有圖片都載入完成後,就不再觸發lazyload函式。
圖片彈層檢視
這個非常簡單了,用一個絕對定位的div去顯示圖片就可以了,div背景色設定成白色(或者是其他你喜歡的顏色),display: none,當點選iframe中的圖片時,設定div裡img標籤src的值,並顯示div。再次點選彈層的圖片,隱藏彈層。
$('.main img').on('click', e => {
$('.img-modal .img-detail').attr('src', e.target.src);
$('.img-modal').show();
});
$('.img-modal').on('click', () => {
$('.img-modal').hide();
});
複製程式碼
父子間通訊
因為在ifame所在的B區也要實現阻尼效果。其他部分的思路和A區的阻尼效果實現思路相同,只不過當使用者到達頁面頂部並下拉到臨界值時,使用postmessage通知父頁面顯示A區,隱藏B區。
iframe中:
$('.main').on('touchend', e => {
//其他邏輯
...
if (last_distance >= RANGE_TOP) {
//切換到上一個頁面
window.parent.postMessage('prev', '*');
...
}
});
複製程式碼
父頁面中:
window.addEventListener('message', rs => {
if (rs.data === 'prev') {
$('.pageB').hide();
$('.pageA').show();
...
}
});
複製程式碼
最終效果
優化
程式碼實現的較為粗糙,還有很多可以優化提高的地方。
- 阻尼效果:AB區實現思路一模一樣,可考慮抽成公共外掛。
- 圖片點選:使用fastclick或zepto對移動端的click事件進行優化。
- 真機除錯:只是在Google開發者工具上除錯了下,沒有在真機上除錯,估計有點相容性問題。
具體程式碼見我的github,並詳細介紹了專案的使用方法。
因為懶,我沒有把這次的程式碼單獨放到一個repo,就和上次的放在一起,實在慚愧。。。