H5頁面PC富文字內容自適應顯示

BeckyWang發表於2018-06-26

上篇部落格,現在增加一個新需求:h5頁面B區顯示富文字HTML片段。

功能描述

  • 要求

    • 有一段PC端顯示的富文字HTML片段,在手機H5頁面B區上載入顯示

    • 保持PC端的樣式縮放適應手機螢幕

    • 如果HTML富文字有圖片

      • 圖片預設不載入
      • 當手機可視區間到B區時候,圖片觸發懶載入顯示
      • 點選富文字圖時候,有彈層載入圖片,可放大檢視
  • 提示

    • 可以考慮用iframe方式處理富文字
    • 但是要考慮到iframe的父子頁面的通訊和相容性
    • 還有考慮到iframe裡富文字圖片懶載入導致高度變化帶來的父頁面的高度自適應

具體實現過程

為什麼選擇iframe

看到需求時,第一想到的是隻是顯示一段富文字內容,為什麼要用iframe,直接顯示不就行了。 iframe還是有挺多缺點的:

  1. 會產生很多頁面,不容易管理
  2. 不容易列印
  3. 瀏覽器的後退按鈕無效
  4. 程式碼複雜,無法被一些搜尋引擎索引到,這一點很關鍵,現在的搜尋引擎爬蟲還不能很好的處理iframe中的內容,所以使用iframe會不利於搜尋引擎優化。
  5. 多數小型的移動裝置(PDA 手機)無法完全顯示框架,裝置相容性差。
  6. 多框架的頁面會增加伺服器的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,就和上次的放在一起,實在慚愧。。。

相關文章