移动端 H5 开发指南 涉及html、css、js三大方向
HTML方向
調用系統功能
<!-- 撥打電話 --> <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>忽略自動識別
<!-- 忽略自動識別電話 --> <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*">喚醒原生應用
<!-- 打開微信 --> <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">禁止頁面緩存
<meta http-equiv="Cache-Control" content="no-cache">禁止字母大寫
<input autocapitalize="off" autocorrect="off">針對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瀏覽器豎屏 --> <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>ios端兼容input光標高度
問題詳情描述:input輸入框光標,在安卓手機上顯示沒有問題,但是在蘋果手機上
當點擊輸入的時候,光標的高度和父盒子的高度一樣。例如下圖,左圖是正常所期待的輸入框光標,右邊是ios的input光標。
解決辦法:高度height和行高line-height內容用padding撐開
例如:
.content{float: left;box-sizing: border-box;height: 88px;width: calc(100% - 240px); .content-input{display: block;box-sizing: border-box;width: 100%;color: #333333;font-size: 28px;//line-height: 88px;padding-top: 20px;padding-bottom: 20px;}}ios端微信h5頁面上下滑動時卡頓、頁面缺失
解決辦法:只需要在公共樣式加入下面這行代碼
ios鍵盤喚起,鍵盤收起以后頁面不歸位
<div class="list-warp"><div class="title"><span>投·被保險人姓名</span></div><div class="content"><input class="content-input" placeholder="請輸入姓名"v-model="peopleList.name"@focus="changefocus()"@blur.prevent="changeBlur()"/> </div></div>changeBlur(){let u = navigator.userAgent, app = navigator.appVersion;let isIOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);if(isIOS){setTimeout(() => {const scrollHeight = document.documentElement.scrollTop || document.body.scrollTop || 0window.scrollTo(0, Math.max(scrollHeight - 1, 0))}, 200)}}安卓彈出的鍵盤遮蓋文本框
解決辦法:給input和textarea標簽添加focus事件,如下,先判斷是不是安卓手機下的操作,當然,可以不用判斷機型,Document 對象屬性和方法,setTimeout延時0.5秒,因為調用安卓鍵盤有一點遲鈍,導致如果不延時處理的話,滾動就失效了
changefocus(){let u = navigator.userAgent, app = navigator.appVersion;let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Linux') > -1;if(isAndroid){setTimeout(function() {document.activeElement.scrollIntoViewIfNeeded();document.activeElement.scrollIntoView();}, 500); } },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。可能在設計圖對應分辨率的移動設備下,背景會完美貼合顯示,但換到其他分辨率的移動設備下就會出現左右空出1px到npx的空隙。此時將background-size聲明為100% 100%,跟隨width和height的變化而變化。反正width和height都是量好的實際尺寸。
.elem {width: 1rem;height: 1rem;background: url("pig.jpg") no-repeat center/100% 100%; }監聽屏幕旋轉
你還在使用JS判斷橫屏豎屏調整樣式嗎?
/* 豎屏 */ @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會讓和無法輸入文本,可對其聲明user-select:auto排除在外。
input, textarea {user-select: auto; }禁止字體調整
* {text-size-adjust: 100%; }禁止高亮顯示
*** {
-webkit-tap-highlight-color: transparent;
}
**
禁止動畫閃屏
在移動設備上添加動畫,多數情況會出現閃屏,給動畫元素的父元素構造一個3D環境就能讓動畫穩定運行了。
.elem {perspective: 1000;backface-visibility: hidden;transform-style: preserve-3d; }美化表單外觀
button, input, select, textarea {appearance: none;/* 自定義樣式 */ }美化滾動占位
滾動條樣式太丑希望自定義,::-webkit-scrollbar-*來幫你。記住以下三個關鍵詞就能隨機應變了。
[x] 「::-webkit-scrollbar」:滾動條整體部分
[x] 「::-webkit-scrollbar-track」:滾動條軌道部分
[x] 「::-webkit-scrollbar-thumb」:滾動條滑塊部分
美化輸入占位
輸入框占位文本太丑,::-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或
,千萬別替換掉,可聲明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,其實它就是移動端版本的jquery。zepto封裝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>全局滾動的影響,當然也可用于局部滾動容器里,因此很值得推廣。
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上特別明顯,簡單概括就是往返頁面無法刷新。
// 在新頁面監聽頁面銷毀事件 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, this.scrollTop); });修復輸入監聽
在蘋果系統上的輸入框輸入文本,keyup/keydown/keypress事件可能會無效。當輸入框監聽keyup事件時,逐個輸入英文和數字會有效,但逐個輸入中文不會有效,需按回車鍵才會有效。 此時可用input事件代替輸入框的keyup/keydown/keypress事件。
簡化回到頂部
const gotopBtn = document.getElementById("gotop-btn"); openBtn.addEventListener("click", () => document.body.scrollIntoView({ behavior: "smooth" }));簡化懶性加載
該函數就是IntersectionObserver,它提供一種異步觀察目標元素及其祖先元素或頂級文檔視窗交叉狀態的方法。詳情可參照MDN文檔,在此不作過多介紹。
懶性加載的第一種使用場景:「圖片懶加載」。只需確認圖片進入可視區域就賦值加載圖片,賦值完成還需對圖片停止監聽。
<img 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("已到底部,請求接口");// 執行接口請求代碼} }) bottom.observe(bottom);優化掃碼識別
通常移動端瀏覽器都會配備長按二維碼圖片識別鏈接的功能,但長按二維碼可能無法識別或錯誤識別。二維碼表面看上去是一張圖片,可二維碼生成方式卻五花八門,二維碼生成方式有以下三種。
[x] 使用<img>渲染 [x] 使用<svg>渲染 [x] 使用<canvas>渲染從網易MTL的測試數據得知,大部分移動端瀏覽器只能識別<img>渲染的二維碼,為了讓全部移動端瀏覽器都能識別二維碼,那只能使用<img>渲染二維碼了。若使用SVG和Canvas的方式生成二維碼,那就想方設法把二維碼數據轉換成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 });SEO
Open Graph Protocol(開放圖譜協議),簡稱 OG 協議
og:image
盡量確保你遵循開放圖形文檔[5]中的所有注釋和示例。此外,一些社交網絡可能會對圖像有特殊要求。例如,[Twitter 要求](https://developer.twitter.com...[6])比例為2:1,最小尺寸為 300x157,最大尺寸為 4096x4096,小于 5MB,JPG、PNG、WEBP 或 GIF 格式。
og:title 最多 35 個字符。
og:description 最多 65 個字符
入門示例
<meta property="og:site_name" content="Colby Fayock" /> <metaproperty="og:title"content="Anyone Can Map! Inspiration and an introduction to the world of mapping - Colby Fayock" /> <metaproperty="og:description"content="Chef Gusteau was a visionary who created food experiences for the world to enjoy. How can we take his lessons and apply them to the world of…" /> <metaproperty="og:url"content="https://www.colbyfayock.com/2020/03/anyone-can-map-inspiration-and-an-introduction-to-the-world-of-mapping/" /> <meta property="og:type" content="article" /> <meta property="article:publisher" content="https://www.colbyfayock.com" /> <meta property="article:section" content="Coding" /> <meta property="article:tag" content="Coding" /> <metaproperty="og:image"content="https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1" /> <metaproperty="og:image:secure_url"content="https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1" /> <meta property="og:image:width" content="1280" /> <meta property="og:image:height" content="640" /> <meta property="twitter:card" content="summary_large_image" /> <metaproperty="twitter:image"content="https://res.cloudinary.com/fay/image/upload/w_1280,h_640,c_fill,q_auto,f_auto/w_860,c_fit,co_rgb:232129,g_west,x_80,y_-60,l_text:Source%20Sans%20Pro_70_line_spacing_-10_semibold:Anyone%20Can%20Map!%20Inspiration%20and%20an%20introduction%20to%20the%20world%20of%20mapping/blog-social-card-1.1" /> <meta property="twitter:site" content="@colbyfayock" />總結
以上是生活随笔為你收集整理的移动端 H5 开发指南 涉及html、css、js三大方向的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 描述 C 库函数 int fseek(
- 下一篇: 路由的query参数