中高級前端必須注意的40條移動端H5坑位指南 | 網易三年實踐

做者:JowayYoung
倉庫:GithubCodePen
博客:官網掘金思否知乎
公衆號:IQ前端
特別聲明:原創不易,未經受權不得轉載或抄襲,如需轉載可聯繫筆者受權css

前言

不知不覺在網易已有三年半,佔了一半時間都在與移動端打交道,整個階段都是遇坑填坑的學習過程。移動端開發在前端裏像神同樣地存在,不是說它多難而是說它坑位實在太多了,怎樣填都填不完。AndroidiOS各顯神通,Android的系統版本和屏幕分辨率多得難以一招兼容,iOS的頑固標準和未知特性多得難以快速掌握。html

三年半沉澱經過本文記錄下所遇到的坑位,或許有些坑位還未遇到,但本文記錄的40條坑位絕對能讓同窗們少走不少彎路,特別是前端小白。爲了減小廢話提升本文質量,對如下內容作一些約定。前端

  • 說起的安卓系統包括Android和基於Android開發的系統
  • 說起的蘋果系統包括iOSiPadOS
  • 本文針對的開發場景是移動端瀏覽器,所以大部分坑位的解決方案在桌面端瀏覽器裏不必定有效
  • 解決方案若未說起適用系統就默認在安卓系統和蘋果系統上都適用,若說起適用系統則會詳細說明
  • Webkit及其衍生內核在移動端瀏覽器市場佔有率裏達到驚人的97%,所以無需太過擔憂CSS3ES6瀏覽器新特性的兼容性
  • 真正的開發環境都是基於webpack構建,所以代碼演示都不會帶上CSS前綴,除非該屬性是Webkit獨有才會帶上-webkit-

每次填坑都是一次實踐過程,所有坑位的源碼都按語言方向記錄在筆者Github上,如有未記錄的坑位可提PR讓筆者合併,給個Star支持下咧!node

原本想爲每一個坑位都截圖錄製GIF做爲演示,但考慮到目前掘金的Markdown編輯器操做圖片還存在缺陷就放棄了,每次上傳圖片都會花費不少時間甚至上傳失敗(望掘金的產品小姐姐和程序小哥哥優化喔)。若需演示只能自行復制代碼了。jquery

HTML方向

調用系統功能

使用<a>能快速調用移動設備的電話/短信/郵件三大通信功能,使用<input>能快速調用移動設備的的圖庫/文件webpack

這些功能方便了頁面與系統的交互,關鍵在於調用格式必定要準確,不然會被移動端瀏覽器忽略。git

<!-- 撥打電話 -->
<a href="tel:10086">撥打電話給10086小姐姐</a>

<!-- 發送短信 -->
<a href="sms:10086">發送短信給10086小姐姐</a>

<!-- 發送郵件 -->
<a href="mailto:young.joway@aliyun.com">發送郵件給JowayYoung</a>

<!-- 選擇照片或拍攝照片 -->
<input type="file" accept="image/*">

<!-- 選擇視頻或拍攝視頻 -->
<input type="file" accept="video/*">

<!-- 多選文件 -->
<input type="file" multiple>
複製代碼
忽略自動識別

有些移動端瀏覽器會自動將數字字母符號識別爲電話/郵箱並將其渲染成上述調用系統功能裏的<a>。雖然很方便卻有可能違背需求。github

<!-- 忽略自動識別電話 -->
<meta name="format-detection" content="telephone=no">

<!-- 忽略自動識別郵箱 -->
<meta name="format-detection" content="email=no">

<!-- 忽略自動識別電話和郵箱 -->
<meta name="format-detection" content="telephone=no, email=no">
複製代碼
彈出數字鍵盤

使用<input type="tel">彈起數字鍵盤會帶上#*,適合輸入電話。推薦使用<input type="number" pattern="\d*">彈起數字鍵盤,適合輸入驗證碼等純數字格式。web

<!-- 純數字帶#和* -->
<input type="tel">

<!-- 純數字 -->
<input type="number" pattern="\d*">
複製代碼
喚醒原生應用

經過location.href與原生應用創建通信渠道,這種頁面與客戶端的通信方式稱爲URL Scheme,其基本格式爲scheme://[path][?query],筆者曾經發表過《H5與App的通信方式》講述URL Scheme的使用。canvas

  • scheme:應用標識,表示應用在系統裏的惟一標識
  • path:應用行爲,表示應用某個頁面或功能
  • query:應用參數,表示應用頁面或應用功能所需的條件參數

URL Scheme通常由前端與客戶端共同協商。喚醒原生應用的前提是必須在移動設備裏安裝了該應用,有些移動端瀏覽器即便安裝了該應用也沒法喚醒原生應用,由於它認爲URL Scheme是一種潛在的危險行爲而禁用它,像Safari微信瀏覽器。還好微信瀏覽器可開啓白名單讓URL Scheme有效。

若在頁面引用第三方原生應用的URL Schema,可經過抓包第三方原生應用獲取其URL

<!-- 打開微信 -->
<a href="weixin://">打開微信</a>

<!-- 打開支付寶 -->
<a href="alipays://">打開支付寶</a>

<!-- 打開支付寶的掃一掃 -->
<a href="alipays://platformapi/startapp?saId=10000007">打開支付寶的掃一掃</a>

<!-- 打開支付寶的螞蟻森林 -->
<a href="alipays://platformapi/startapp?appId=60000002">打開支付寶的螞蟻森林</a>
複製代碼
禁止頁面縮放

在智能手機的普及下,不少網站都具有桌面端移動端兩種瀏覽版本,所以無需雙擊縮放查看頁面。禁止頁面縮放可保障移動端瀏覽器能無遺漏地展示頁面全部佈局。

<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, minimum-scale=1, maximum-scale=1">
複製代碼
禁止頁面緩存

Cache-Control指定請求和響應遵循的緩存機制,不想使用瀏覽器緩存就禁止唄!

<meta http-equiv="Cache-Control" content="no-cache">
複製代碼
禁止字母大寫

有時在輸入框裏輸入文本會默認開啓首字母大寫糾正,就是輸入首字母小寫會被自動糾正成大寫,特麼的煩。直接聲明autocapitalize=off關閉首字母大寫功能和autocorrect=off關閉糾正功能。

<input autocapitalize="off" autocorrect="off">
複製代碼
針對Safari配置

貼一些Safari較零散且少用的配置。

<!-- 設置Safari全屏,在iOS7+無效 -->
<meta name="apple-mobile-web-app-capable" content="yes">

<!-- 改變Safari狀態欄樣式,可選default/black/black-translucent,需在上述全屏模式下才有效 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black">

<!-- 添加頁面啓動佔位圖 -->
<link rel="apple-touch-startup-image" href="pig.jpg" media="(device-width: 375px)">

<!-- 保存網站到桌面時添加圖標 -->
<link rel="apple-touch-icon" sizes="76x76" href="pig.jpg">

<!-- 保存網站到桌面時添加圖標且清除默認光澤 -->
<link rel="apple-touch-icon-precomposed" href="pig.jpg">
複製代碼
針對其餘瀏覽器配置

貼一些其餘瀏覽器較零散且少用的配置,主要是經常使用的QQ瀏覽器UC瀏覽器360瀏覽器。從網易MTL的測試數據得知,新版的QQ瀏覽器UC瀏覽器已不支持如下<meta>聲明瞭。

<!-- 強制QQ瀏覽器豎屏 -->
<meta name="x5-orientation" content="portrait">

<!-- 強制QQ瀏覽器全屏 -->
<meta name="x5-fullscreen" content="true">

<!-- 開啓QQ瀏覽器應用模式 -->
<meta name="x5-page-mode" content="app">

<!-- 強制UC瀏覽器豎屏 -->
<meta name="screen-orientation" content="portrait">

<!-- 強制UC瀏覽器全屏 -->
<meta name="full-screen" content="yes">

<!-- 開啓UC瀏覽器應用模式 -->
<meta name="browsermode" content="application">

<!-- 開啓360瀏覽器極速模式 -->
<meta name="renderer" content="webkit">
複製代碼
讓:active有效,讓:hover無效

有些元素的:active可能會無效,而元素的:hover在點擊後會一直處於點擊狀態,需點擊其餘位置才能解除點擊狀態。給<body>註冊一個空的touchstart事件可將兩種狀態反轉。

<body ontouchstart></body>
複製代碼

CSS方向

自動適應佈局

針對移動端,筆者一般會結合JS依據屏幕寬度設計圖寬度的比例動態聲明<html>font-size,以rem爲長度單位聲明全部節點的幾何屬性,這樣就能作到大部分移動設備的頁面兼容,兼容出入較大的地方再經過媒體查詢作特別處理。

筆者一般將rem佈局比例設置成1rem=100px,即在設計圖上100px長度在CSS代碼上使用1rem表示。

function AutoResponse(width = 750) {
    const target = document.documentElement;
    if (target.clientWidth >= 600) {
        target.style.fontSize = "80px";
    } else {
        target.style.fontSize = target.clientWidth / width * 100 + "px";
    }
}
AutoResponse();
window.addEventListener("resize", () => AutoResponse());
複製代碼

固然還可依據屏幕寬度設計圖寬度的比例使用calc()動態聲明<html>font-size,這樣就能節省上述代碼。不對,是徹底代替上述代碼。

html {
    font-size: calc(100vw / 7.5);
}
複製代碼

若以iPad Pro分辨率1024px爲移動端和桌面端的斷點,還可結合媒體查詢作斷點處理。1024px如下使用rem佈局,不然不使用rem佈局

@media screen and (max-width: 1024px) {
    html {
        font-size: calc(100vw / 7.5);
    }
}
複製代碼
自動適應背景

使用rem佈局聲明一個元素背景,多數狀況會將background-size聲明爲cover。可能在設計圖對應分辨率的移動設備下,背景會完美貼合顯示,但換到其餘分辨率的移動設備下就會出現左右空出1pxnpx的空隙。

此時將background-size聲明爲100% 100%,跟隨widthheight的變化而變化。反正widthheight都是量好的實際尺寸。

.elem {
    width: 1rem;
    height: 1rem;
    background: url("pig.jpg") no-repeat center/100% 100%;
}
複製代碼
監聽屏幕旋轉

你還在使用JS判斷橫屏豎屏調整樣式嗎?那就真的Out了。

/* 豎屏 */
@media all and (orientation: portrait) {
    /* 自定義樣式 */
}
/* 橫屏 */
@media all and (orientation: landscape) {
    /* 自定義樣式 */
}
複製代碼
支持彈性滾動

在蘋果系統上非<body>元素的滾動操做可能會存在卡頓,但安卓系統不會出現該狀況。經過聲明overflow-scrolling:touch調用系統原生滾動事件優化彈性滾動,增長頁面滾動的流暢度。

body {
    -webkit-overflow-scrolling: touch;
}
.elem {
    overflow: auto;
}
複製代碼
禁止滾動傳播

桌面端瀏覽器不同,移動端瀏覽器有一個奇怪行爲。當頁面包含多個滾動區域時,滾完一個區域後若還存在滾動動量則會將這些剩餘動量傳播到下一個滾動區域,形成該區域也滾動起來。這種行爲稱爲滾動傳播

若不想產生這種奇怪行爲可直接禁止。

.elem {
    overscroll-behavior: contain;
}
複製代碼
禁止屏幕抖動

對於一些忽然出現滾動條的頁面,可能會產生左右抖動的不良影響。在一個滾動容器裏,打開彈窗就隱藏滾動條,關閉彈窗就顯示滾動條,來回操做會讓屏幕抖動起來。提早聲明滾動容器的padding-right爲滾動條寬度,就能有效消除這個不良影響。

每一個移動端瀏覽器的滾動條寬度都有可能不一致,甚至不必定佔位置,經過如下方式能間接計算出滾動條的寬度。100vw爲視窗寬度,100%爲滾動容器內容寬度,相減就是滾動條寬度,妥妥的動態計算。

body {
    padding-right: calc(100vw - 100%);
}
複製代碼
禁止長按操做

有時不想用戶長按元素呼出菜單進行點連接打電話發郵件保存圖片掃描二維碼等操做,聲明touch-callout:none禁止用戶長按操做。

有時不想用戶複製粘貼盜文案,聲明user-select:none禁止用戶長按操做和選擇複製。

* {
    /* pointer-events: none; */ /* 微信瀏覽器還需附加該屬性纔有效 */
    user-select: none; /* 禁止長按選擇文字 */
    -webkit-touch-callout: none;
}
複製代碼

但聲明user-select:none會讓<input><textarea>沒法輸入文本,可對其聲明user-select:auto排除在外。

input,
textarea {
    user-select: auto;
}
複製代碼
禁止字體調整

旋轉屏幕可能會改變字體大小,聲明text-size-adjust:100%讓字體大小保持不變。

* {
    text-size-adjust: 100%;
}
複製代碼
禁止高亮顯示

觸摸元素會出現半透明灰色遮罩,不想要!

* {
    -webkit-tap-highlight-color: transparent;
}
複製代碼
禁止動畫閃屏

在移動設備上添加動畫,多數狀況會出現閃屏,給動畫元素的父元素構造一個3D環境就能讓動畫穩定運行了。

.elem {
    perspective: 1000;
    backface-visibility: hidden;
    transform-style: preserve-3d;
}
複製代碼
美化表單外觀

表單元素樣式太醜但願自定義,appearance:none來幫你。

button,
input,
select,
textarea {
    appearance: none;
    /* 自定義樣式 */
}
複製代碼
美化滾動佔位

滾動條樣式太醜但願自定義,::-webkit-scrollbar-*來幫你。記住如下三個關鍵詞就能隨機應變了。

  • ::-webkit-scrollbar:滾動條總體部分
  • ::-webkit-scrollbar-track:滾動條軌道部分
  • ::-webkit-scrollbar-thumb:滾動條滑塊部分
::-webkit-scrollbar {
    width: 6px;
    height: 6px;
    background-color: transparent;
}
::-webkit-scrollbar-track {
    background-color: transparent;
}
::-webkit-scrollbar-thumb {
    border-radius: 3px;
    background-image: linear-gradient(135deg, #09f, #3c9);
}
複製代碼
美化輸入佔位

輸入框佔位文本太醜,::-webkit-input-placeholder來幫你。

input::-webkit-input-placeholder {
    color: #66f;
}
複製代碼
對齊輸入佔位

有強迫症的同窗總會以爲輸入框文本位置總體偏上,感受未居中內心就癢癢的。桌面端瀏覽器裏聲明line-height等於height就能解決,但移動端瀏覽器裏仍是未能解決,需將line-height聲明爲normal才行。

input {
    line-height: normal;
}
複製代碼
對齊下拉選項

下拉框選項默認向左對齊,是時候改改向右對齊了。

select option {
    direction: rtl;
}
複製代碼
修復點擊無效

在蘋果系統上有些狀況下非可點擊元素監聽click事件可能會無效,針對該狀況只需對不觸發click事件的元素聲明cursor:pointer就能解決。

.elem {
    cursor: pointer;
}
複製代碼
識別文本換行

多數狀況會使用JS換行文本,那就真的Out了。若接口返回字段包含\n<br>,千萬別替換掉,可聲明white-space:pre-line交由瀏覽器作斷行處理。

* {
    white-space: pre-line;
}
複製代碼
開啓硬件加速

想動畫更流暢嗎,開啓GPU硬件加速唄!

.elem {
    transform: translate3d(0, 0, 0);
    /* transform: translateZ(0); */
}
複製代碼
描繪像素邊框

萬年話題,如何描繪一像素邊框

.elem {
    position: relative;
    width: 200px;
    height: 80px;
    &::after {
        position: absolute;
        left: 0;
        top: 0;
        border: 1px solid #f66;
        width: 200%;
        height: 200%;
        content: "";
        transform: scale(.5);
        transform-origin: left top;
    }
}
複製代碼
控制溢出文本

萬年話題,如何控制文本作單行溢出多行溢出

.elem {
    width: 400px;
    line-height: 30px;
    font-size: 20px;
    &.sl-ellipsis {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    &.ml-ellipsis {
        display: -webkit-box;
        overflow: hidden;
        text-overflow: ellipsis;
        -webkit-line-clamp: 3;
        -webkit-box-orient: vertical;
    }
}
複製代碼

JS方向

禁止點擊穿透

移動端瀏覽器裏點擊操做會存在300ms延遲,每每會形成點擊延遲甚至點擊無效,這個是衆所周知的事情。

2007年蘋果發佈首款iPhone搭載的Safari爲了將桌面端網站能較好地展現在移動端瀏覽器上而使用了雙擊縮放。該方案就是上述300ms延遲的主要緣由,當用戶執行第一次單擊後會預留300ms檢測用戶是否繼續執行單擊,如果則執行縮放操做,若不然執行點擊操做。鑑於該方案的成功,其餘移動端瀏覽器也複製了該方案,如今幾乎全部移動端瀏覽器都配備該功能。而該方案引起的點擊延遲被稱爲點擊穿透

在前端領域裏最先解決點擊穿透是jQuery時代zepto,估計如今大部分同窗都未使用過zepto,其實它就是移動端版本的jqueryzepto封裝tap事件能有效地解決點擊穿透,經過監聽document上的touch事件完成tap事件的模擬,並將tap事件冒泡到document上觸發。

移動端瀏覽器上不使用click事件而使用touch事件是由於click事件有着明顯的延遲,後續又出現fastclick。該解決方案監聽用戶是否作了雙擊操做,可正常使用click事件,而點擊穿透就交給fastclick自動判斷。更多fastclick原理可自行百度,在此不做過多介紹。

fastclick有現成的NPM包,可直接安裝到項目裏。引入fastclick可以使用click事件代替tap事件,接入方式極其簡單。

import Fastclick from "fastclick";

FastClick.attach(document.body);
複製代碼
禁止滑動穿透

移動端瀏覽器裏出現彈窗時,若在屏幕上滑動能觸發彈窗底下的內容跟着滾動,這個是衆所周知的事情。

首先明確解決滑動穿透需保持哪些交互行爲,那就是除了彈窗內容能點擊或滾動,其餘內容都不能點擊或滾動。目前不少解決方案都沒法作到這一點,所有解決方案都能禁止<body>的滾動行爲卻引起其餘問題。

  • 彈窗打開後內部內容沒法滾動
  • 彈窗關閉後頁面滾動位置丟失
  • Webview能上下滑動露出底色

當打開彈窗時給<body>聲明position:fixed;left:0;width:100%並動態聲明top。聲明position:fixed會致使<body>滾動條消失,此時會發現雖然無滑動穿透,但頁面滾動位置早已丟失。經過scrollingElement獲取頁面當前滾動條偏移量並將其取負值且賦值給top,那麼在視覺上就無任何變化。當關閉彈窗時移除position:fixed;left:0;width:100%和動態top

scrollingElement可兼容地獲取scrollTopscrollHeight等屬性,在移動端瀏覽器裏屢試不爽。document.scrollingElement.scrollHeight可完美代替曾經的document.documentElement.scrollHeight || document.body.scrollHeight,一眼看上去就是代碼減小了。

該解決方案在視覺上無任何變化,完爆其餘解決方案,其實就是一種反向思惟和障眼法。該解決方案完美解決固定彈窗滾動彈窗<body>全局滾動的影響,固然也可用於局部滾動容器裏,所以很值得推廣。

body.static {
    position: fixed;
    left: 0;
    width: 100%;
}
複製代碼
const body = document.body;
const openBtn = document.getElementById("open-btn");
const closeBtn = document.getElementById("close-btn");
openBtn.addEventListener("click", e => {
    e.stopPropagation();
    const scrollTop = document.scrollingElement.scrollTop;
    body.classList.add("static");
    body.style.top = `-${scrollTop}px`;
});
closeBtn.addEventListener("click", e => {
    e.stopPropagation();
    body.classList.remove("static");
    body.style.top = "";
});
複製代碼
支持往返刷新

點擊移動端瀏覽器前進按鈕後退按鈕,有時不會自動執行舊頁面的JS代碼,這與往返緩存有關。這種狀況在Safari上特別明顯,簡單歸納就是往返頁面沒法刷新。

往返緩存指瀏覽器爲了在頁面間執行前進後退操做時能擁有更流暢體驗的一種策略,如下簡稱BFCache。該策略具體表現爲:當用戶前往新頁面前將舊頁面的DOM狀態保存在BFCache裏,當用戶返回舊頁面前將舊頁面的DOM狀態從BFCache裏取出並加載。大部分移動端瀏覽器都會部署BFCache,可大大節省接口請求的時間和帶寬。

瞭解什麼是BFCache再對症下藥,解決方案就在window.onunload上作文章。

// 在新頁面監聽頁面銷燬事件
window.addEventListener("onunload", () => {
    // 執行舊頁面代碼
});
複製代碼

若在Vue SPA上使用keep-alive也不能讓頁面刷新,可將接口請求放到beforeRouteEnter()裏。

固然還有另外一種解決方案。pageshow事件在每次頁面加載時都會觸發,不管是首次加載仍是再次加載都會觸發,這就是它與load事件的區別。pageshow事件暴露的persisted可判斷頁面是否從BFCache裏取出。

window.addEventListener("pageshow", e => e.persisted && location.reload());
複製代碼

若瀏覽器不使用<meta http-equiv="Cache-Control" content="no-cache">禁用緩存,該解決方案仍是很值得一用。

解析有效日期

在蘋果系統上解析YYYY-MM-DD HH:mm:ss這種日期格式會報錯Invalid Date,但在安卓系統上解析這種日期格式徹底無問題。

new Date("2019-03-31 21:30:00"); // Invalid Date
複製代碼

查看Safari相關開發手冊發現可用YYYY/MM/DD HH:mm:ss這種日期格式,簡單歸納就是年月日必須使用/銜接而不能使用-銜接。固然安卓系統也支持該格式,然而接口返回字段的日期格式一般是YYYY-MM-DD HH:mm:ss,那麼需替換其中的-/

const date = "2019-03-31 21:30:00";
new Date(date.replace(/\-/g, "/"));
複製代碼
修復高度坍塌

當頁面同時出現如下三個條件時,鍵盤佔位會把頁面高度壓縮一部分。當輸入完成鍵盤佔位消失後,頁面高度有可能回不到原來高度,產生坍塌致使Webview底色露臉,簡單歸納就是輸入框失焦後頁面未回彈。

  • 頁面高度太小
  • 輸入框在頁面底部或視窗中下方
  • 輸入框聚焦輸入文本

只要保持先後滾動條偏移量一致就不會出現上述問題。在輸入框聚焦時獲取頁面當前滾動條偏移量,在輸入框失焦時賦值頁面以前獲取的滾動條偏移量,這樣就能間接還原頁面滾動條偏移量解決頁面高度坍塌。

const input = document.getElementById("input");
let scrollTop = 0;
input.addEventListener("focus", () => {
    scrollTop = document.scrollingElement.scrollTop;
});
input.addEventListener("blur", () => {
    document.scrollingElement.scrollTo(0, scrollTop);
});
複製代碼
修復輸入監聽

在蘋果系統上的輸入框輸入文本,keyup/keydown/keypress事件可能會無效。當輸入框監聽keyup事件時,逐個輸入英文和數字會有效,但逐個輸入中文不會有效,需按回車鍵纔會有效。

此時可用input事件代替輸入框的keyup/keydown/keypress事件

簡化回到頂部

曾幾什麼時候編寫一個返回頂部函數麻煩得要死,需scrollTop定時器條件判斷三者配合才能完成。其實DOM對象裏隱藏了一個很好用的函數可完成上述功能,一行核心代碼就能搞定。

該函數就是scrollIntoView,它會滾動目標元素的父容器使之對用戶可見,簡單歸納就是相對視窗讓容器滾動到目標元素位置。它有三個可選參數能讓scrollIntoView滾動起來更優雅。

  • behavior:動畫過渡效果,默認auto無,可選smooth平滑
  • inline:水平方向對齊方式,默認nearest就近對齊,可選start頂部對齊center中間對齊end底部對齊
  • block:垂直方向對齊方式,默認start頂部對齊,可選center中間對齊end底部對齊nearest就近對齊
const gotopBtn = document.getElementById("gotop-btn");
openBtn.addEventListener("click", () => document.body.scrollIntoView({ behavior: "smooth" }));
複製代碼

固然還可滾動到目標元素位置,只需將document.body修改爲目標元素的DOM對象。一行核心代碼就能搞掂的事情爲什麼還編寫那麼多代碼去完成,不累嗎?

簡化懶性加載

與上述簡化回到頂部同樣,編寫一個懶性加載函數也一樣需scrollTop定時器條件判斷三者配合才能完成。其實DOM對象裏隱藏了一個很好用的函數可完成上述功能,該函數無需監聽容器的scroll事件,經過瀏覽器自身機制完成滾動監聽。

該函數就是IntersectionObserver,它提供一種異步觀察目標元素及其祖先元素或頂級文檔視窗交叉狀態的方法。詳情可參照MDN文檔,在此不做過多介紹。

懶性加載的第一種使用場景:圖片懶加載。只需確認圖片進入可視區域就賦值加載圖片,賦值完成還需對圖片中止監聽。

<img data-src="pig.jpg">
<!-- 不少<img> -->
複製代碼
const imgs = document.querySelectorAll("img.lazyload");
const observer = new IntersectionObserver(nodes => {
    nodes.forEach(v => {
        if (v.isIntersecting) { // 判斷是否進入可視區域
            v.target.src = v.target.dataset.src; // 賦值加載圖片
            observer.unobserve(v.target); // 中止監聽已加載的圖片
        }
    });
});
imgs.forEach(v => observer.observe(v));
複製代碼

懶性加載的第二種使用場景:下拉加載。在列表最底部部署一個佔位元素且該元素無任何高度或實體外觀,只需確認佔位元素進入可視區域就請求接口加載數據。

<ul>
    <li></li>
    <!-- 不少<li> -->
</ul>
<!-- 也可將#bottom以<li>的形式插入到<ul>內部的最後位置 -->
<div id="bottom"></div>
複製代碼
const bottom = document.getElementById("bottom");
const observer = new IntersectionObserver(nodes => {
    const tgt = nodes[0]; // 反正只有一個
    if (tgt.isIntersecting) {
        console.log("已到底部,請求接口");
        // 執行接口請求代碼
    }
})
observer.observe(bottom);
複製代碼
優化掃碼識別

一般移動端瀏覽器都會配備長按二維碼圖片識別連接的功能,但長按二維碼可能沒法識別或錯誤識別。二維碼錶面看上去是一張圖片,可二維碼生成方式卻五花八門,二維碼生成方式有如下三種。

  • 使用<img>渲染
  • 使用<svg>渲染
  • 使用<canvas>渲染

從網易MTL的測試數據得知,大部分移動端瀏覽器只能識別<img>渲染的二維碼,爲了讓所有移動端瀏覽器都能識別二維碼,那隻能使用<img>渲染二維碼了。若使用SVGCanvas的方式生成二維碼,那就千方百計把二維碼數據轉換成Base64再賦值到<img>src上。

一個頁面可能存在多個二維碼,若長按二維碼只能識別最後一個,那隻能控制每一個頁面只存在一個二維碼。

自動播放媒體

常見媒體元素包括音頻<audio>和視頻<video>,爲了讓用戶獲得更好的媒體播放體驗與不盲目浪費用戶流量,大部分移動端瀏覽器都明確規定不能自動播放媒體或默認屏蔽autoplay。爲了能讓媒體在頁面加載完成後自動播放,只能顯式聲明播放。

const audio = document.getElementById("audio");
const video = document.getElementById("video");
audio.play();
video.play();
複製代碼

對於像微信瀏覽器這樣的內置瀏覽器,還需監聽其應用SDK加載完成才能觸發上述代碼,以保障WebView正常渲染。其餘內置瀏覽器同理,在此不做過多介紹。

document.addEventListener("WeixinJSBridgeReady", () => {
    // 執行上述媒體自動播放代碼
});
複製代碼

在蘋果系統上明確規定用戶交互操做開始後才能播放媒體,未獲得用戶響應會被Safari自動攔截,所以需監聽用戶首次觸摸操做並觸發媒體自動播放,而該監聽僅此一次。

document.body.addEventListener("touchstart", () => {
    // 執行上述媒體自動播放代碼
}, { once: true });
複製代碼

總結

如有未記錄的坑位可提PR讓筆者合併,咱們一塊兒記錄更多的移動端坑位,讓更多前端開發者少走彎路。本文也是筆者2021年在掘金社區的首篇文章,但願今年能產出更多高質量文章。從筆者角度上看,一篇文章不是技術多牛逼筆風多優雅才概括爲好文章,而是總體內容能幫助本身和更多人進步纔算是好文章。向這個方向進發,爲本身打Call。

上述坑位按照HTML方向CSS方向JS方向三大類型劃分,能更高效地區分出每一個坑位的使用場景和解決方案,減小混亂記憶。當中說起了CSS方向的不少坑位,這些坑位也屬於一些CSS開發技巧,若喜歡CSS的同窗可瞭解筆者上架的掘金社區首本CSS小冊《玩轉CSS的藝術之美》作更深一步學習。

回看筆者往期高贊文章,也許能收穫更多喔!

最近筆者參加了2020創做者榜單,喜歡筆者可爲筆者打Call喔!你一票我一票,居居上榜靠你們❤️!

上榜

結語

❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更多高質量文章

關注公衆號IQ前端,一個專一於CSS/JS開發技巧的前端公衆號,更多前端小乾貨等着你喔

  • 關注後回覆資料免費領取學習資料
  • 關注後回覆進羣拉你進技術交流羣
  • 歡迎關注IQ前端,更多CSS/JS開發技巧只在公衆號推送

相關文章
相關標籤/搜索