【學習筆記】 關於 iOS Safari 的那些坑:禁止選取 & 縮放設定

更新於 發佈於 閱讀時間約 10 分鐘
這次專案開發是針對 iOS 系統,需要解決畫面縮放的問題,有些關鍵字來回搜尋好多遍,卻還是容易搞混,於是乎乾脆記錄下來,日後如果遇到這類型問題,也會直接更新在這篇。

A. 前言:進入 meta 元宇宙

meta tag 是網頁 HTML 架構中的一種描述標籤,提供網頁的內容資訊給瀏覽器或搜尋引擎,語法如下:
<meta name="參數" content="具體參數值">
在 android 系統與 iOS 10 以前的行動裝置,透過設定 HTML meta tag 的 viewport(可視區域),我們能控制畫面顯示的寬高、縮放比例以及是否允許縮放。

B. Zoom in/out on mobile device

B-0. user-scalable=no:禁止縮放畫面

一般而言,在行動裝置的瀏覽器中,使用者可透過以下手勢來達到縮放頁面(zoom-in/zoom-out)的效果:
  • pinch:兩指縮放
  • double-tap:雙擊縮放
若在網頁的 <head> 區塊加入以下屬性,即可達到「禁止使用者縮放畫面」的效果:
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">

// 等同於
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
上述 content 屬性說明:
  • width=device-width:設定瀏覽器頁面的寬度同裝置的寬度,即自動符合不同手機螢幕預設的最佳解析度
  • initial-scale=1.0:設定畫面初始縮放比例為 100%,即不放大也不縮小
  • minimum-scale=1, maximum-scale=1:設定畫面最小和最大的縮放比例,均為 1 代表無法縮放
  • user-scalable=0:不允許使用者改變縮放比例
但到 iOS 10 之後的版本,為了提高 Safari 網頁的輔助功能,便忽略 meta 設定中的 user-scalable=no 屬性,使得「不允許使用者改變縮放比例」這項限制失效,以下是官網原文
To improve accessibility on websites in Safari, users can now pinch-to-zoom even when a website sets user-scalable=no in the viewport.
為了提高 Safari 網站中的輔助功能,即使在網站中的 viewport 設定 user-scalable=no,用戶仍可用手指進行縮放。
查了資料發現 meta viewport 特性原來最早就是由 Apple 公司引入,旨在解決不同行動裝置畫面顯示的問題;到現在選擇忽略 user-scalable=no,雖然本意是好的,讓使用者能夠自由縮放畫面至合適的大小,卻也因此失去開發網頁的彈性。
但山不轉人轉,我們還是能透過其他語法來達到想要的結果,以下舉幾個範例:
  • CSS 屬性 touch-action: manipulation:禁止 double-tap
  • 監聽 JS 事件 touchstart/touchend:禁止 pinch & double-tap
  • 監聽 JS 事件gesturestart:禁止 pinch

B-1. CSS 解法:以 touch-action: manipulation 禁止 double-tap

加上 touch-action: manipulation 屬性,讓元素只能使用滑動或兩指縮放,即忽略 double tap 手勢的縮放:
html, body {
touch-action: manipulation;
}

B-2. JS 解法:handle touchstart/touchend event

監聽這兩個 event 原理如下:
  • touchstart:透過偵測 e.touches.length(觸控點數目)handle pinch 手勢
document.addEventListener('touchstart', (event) => {
if (event.touches && event.touches.length > 1) { // 禁止多指觸控
event.preventDefault();
}
}, { passive: false });
由於 iOS 11.1 版本的變動,預設 passive: true 以提高使用性能,但也因此造成 e.preventDefault() 語法失效。因此需加上 { passive: false } 屬性,主動告訴瀏覽器這裡的監聽將使用 event.preventDefault(),即可阻止事件後面的動作。
  • touchend:透過偵測點擊時間差 handle double-tap 手勢
let lastTouchEndTime = 0;
document.addEventListener('touchend', (event) => {
const now = new Date().getTime();
if((now - lastTouchEndTime) <= 300) { // 偵測時間差是否小於 300ms
event.preventDefault();
}
lastTouchEndTime = now;
}, false);
之所以 touchend 會取 300ms 時間差,是因為每一次在使用者 touch 螢幕時,都會產生 300ms 的延遲去監聽是否觸發 double-tap。在兩次 touch 之間的時間差小於 300ms 的情況下執行 event.preventDefault(),即可取消 double-tap 動作所觸發的縮放效果。

B-3. JS 解法:gesturestart event 兩指以上事件觸發

可參考 MDN 文件 Element: gesturestart event - Web APIs | MDN (mozilla.org),和 touchEvent 類似,但僅適用於 iOS 系統。
gesturestart 會在兩指以上觸碰螢幕時觸發,即可忽略 pinch tap 手勢的兩指縮放:
// [Safari only] gesturestart event: multi finger gestures touching 
document.addEventListener('gesturestart', function(event) {
// 阻止兩指縮放畫面
event.preventDefault();
});

C. 其他想記錄的

C-1. mouseEvent vs touchEvent vs gesturestart

  • mouseEvent 一次只能有一個觸擊點
  • touchEvent 支援多點觸控
  • gesturestart 兩指以上觸碰時觸發,僅 iOS 支援

C-2. user-select: none:禁止區塊反白選取

即雙點擊不會選取到文字區塊:
* {
user-select: none;
-webkit-user-select: none; /* Chrome Safari */
-moz-user-select: none; /* Firefox */
}

D. 小結

如果被問到工作上曾遇過什麼樣的困難,除了溝通技巧,我想「跨平台開發」肯定能名列前茅。
畢竟光是不同作業系統(如:Windows、Mac、Android、iOS)、不同行動裝置(如:Pixel、Samsung、iPhone、iPad)、不同版本(如:Android 10-14、iOS 10-16.3),甚至是不同瀏覽器(如:Safari、Chrome、Firefox、Edge)等,都可能出現非預期的結果,有時不一定是程式碼有錯誤,而是版本不支援導致,必須仰賴多方測試,才能確定是否能夠兼容各個平台系統。
想當然耳,一名工程師哪可能同時擁有這麼多種裝置,要面面俱到尤其困難,更多的情況是「遇到問題再說」,或盡可能吸收前人的智慧,避免再踩到類似的坑。

E. Reference

Originally published at https://heidiliu2020.github.io on February 9, 2023.
avatar-img
0會員
7內容數
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
Heidi Liu的沙龍 的其他內容
What is WebSocket? WebSocket 是 HTML5 提供的一種網路傳輸協定,是瀏覽器(Client)與伺服器(Server)交換資料的方式之一。 與我們較為熟知的 HTTP 或 HTTPS 協定,同樣位於 OSI 模型的應用層,且基於傳輸層的 TCP 協定。
過去在程式導師實驗課程中,整理過這兩篇筆記探討「測試」是怎麼回事: [week 3] 初探 Jest:如何測試程式? [week 22] React:用 SPA 架構實作一個部落格(三)- 淺談測試 在轉職後的第一家公司,組內曾嘗試在既有專案中撰寫測試,卻因時程緊湊而不了了之。
「平凡的我啊,哪有時間低頭回望?」 — — 《排球少年 264 話》 各位安安,我是 Heidi,從年前就一直思考,應該如何總結這一年來的學習心得,結果又拖到現在,不知不覺都已經入職快兩個月了XD 這篇是記錄我在 Lidemy 學習半年的結業心得,也會稍微帶到多人協作和求職總結。
“Do not, for one repulse, give up the purpose that you resolved to effect.” — William Shakespeare. 幾天前分享的上篇心得有稍微提到,前半段會偏向描寫過去的個人經驗,是有關於在開始學習程式之前的自己
“Never regret. If it’s good, it’s wonderful. If it’s bad, it’s experience. “ — Victoria Holt. 前言 從六月初開始,參加第四期程式導師計畫以來,這段日子過得飛快,轉眼間一個禮拜又過去,自己也迎來了課程中場。
“If you wish to reach the highest, begin at the lowest.” — Publilius Syrus.
What is WebSocket? WebSocket 是 HTML5 提供的一種網路傳輸協定,是瀏覽器(Client)與伺服器(Server)交換資料的方式之一。 與我們較為熟知的 HTTP 或 HTTPS 協定,同樣位於 OSI 模型的應用層,且基於傳輸層的 TCP 協定。
過去在程式導師實驗課程中,整理過這兩篇筆記探討「測試」是怎麼回事: [week 3] 初探 Jest:如何測試程式? [week 22] React:用 SPA 架構實作一個部落格(三)- 淺談測試 在轉職後的第一家公司,組內曾嘗試在既有專案中撰寫測試,卻因時程緊湊而不了了之。
「平凡的我啊,哪有時間低頭回望?」 — — 《排球少年 264 話》 各位安安,我是 Heidi,從年前就一直思考,應該如何總結這一年來的學習心得,結果又拖到現在,不知不覺都已經入職快兩個月了XD 這篇是記錄我在 Lidemy 學習半年的結業心得,也會稍微帶到多人協作和求職總結。
“Do not, for one repulse, give up the purpose that you resolved to effect.” — William Shakespeare. 幾天前分享的上篇心得有稍微提到,前半段會偏向描寫過去的個人經驗,是有關於在開始學習程式之前的自己
“Never regret. If it’s good, it’s wonderful. If it’s bad, it’s experience. “ — Victoria Holt. 前言 從六月初開始,參加第四期程式導師計畫以來,這段日子過得飛快,轉眼間一個禮拜又過去,自己也迎來了課程中場。
“If you wish to reach the highest, begin at the lowest.” — Publilius Syrus.
你可能也想看
Google News 追蹤
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
蘋果近日獲得一項與iPhone電池技術相關的新專利,將有助於提升手機的續航性能, 訴求可在不增加機身體積與重量的前提下,就能塞進更大容量的電池,被外媒視為是一大突破性的創新技術。 據外媒Gizchina 報導指出,該技術被命名為「Battery Cells with Tabs at Righ
Thumbnail
蘋果近日獲得一項與iPhone電池技術相關的新專利,將有助於提升手機的續航性能, 訴求可在不增加機身體積與重量的前提下,就能塞進更大容量的電池,被外媒視為是一大突破性的創新技術。 據外媒Gizchina 報導指出,該技術被命名為「Battery Cells with Tabs at Righ
Thumbnail
這篇文章探討瞭如何在iOS應用程式中客製化Alert,包括改變字體大小、內嵌連結以及讓Alert的高度隨著字數增長並提供scroll操作。同時使用SwiftUI進行客製化,並介紹瞭解決高度超出範圍後文字捲動與scrollView固定高度的方法。
Thumbnail
iOS 18 新增了原生 App 上鎖和隱藏功能,可以在打開應用程式增加驗證身分和隱藏敏感程式,即使將手機借給他人,對方沒有密碼也無法打開已上鎖的 App。
Thumbnail
iPhone 最新作業系統 iOS 18 控制中心大更新,預設有常用功能、音樂播放器、連線控制三個頁面,並允許使用者自訂頁面,可以直接在控制中心管理所需的功能、調整位置和選擇尺寸。
Thumbnail
iOS15推出了新的按鈕外觀設定功能,讓設定變得更加直觀。使用不同的圓角設定和圖片位置可以輕鬆創建不同風格的按鈕。另外,新的選取效果變化功能也讓按鈕設計更加靈活。本文將介紹這些新功能的使用方法和效果。
Thumbnail
作者 Only 系列文章,【一天一千字,進化每一次】手機容量大(ROM)是否智商稅? 選擇Apple的用戶是否值得買大容量手機?或是Google雲端費用是否更划算? 該不該直上頂規?手機選擇大銀幕,小銀幕,摺疊螢幕?本文從不同角度提供了選購手機及作業系統的參考建議。
Thumbnail
在這個大螢幕手機滿街跑的時代,小螢幕手機似乎成了稀有品,目前大多數螢幕尺寸就是6吋起跳,很難看到6吋以下的手機,並且許多廠商也不太願意繼續推出這類的產品,所以也就逐漸淡出了一般消費者的視野這篇,自然而然的小螢幕手機似乎就變成了小眾產品。 那......如果再次拿起小螢幕手機呢?
Thumbnail
避免網頁在不同瀏覽器中長不一樣。用 CSS Reset 來「重置」和統一瀏覽器預設樣式。
Thumbnail
由於iphone的「捷徑」功能實在太方便,連續幾篇都在探索還有什麼應用,這篇加上「動作按鈕」使用,讓捷徑功能更強大! 首先,預設的動作按鈕開啟相機功能需要進入UI畫面,然後再進行拍照,但有時畫面一閃而過,根本來不及捕捉。因此,今天分享一個簡化拍照動作的方法,只需一鍵完成拍照。 步驟如下: 先建
Thumbnail
/ 大家現在出門買東西還會帶錢包嗎 鴨鴨發現自己好像快一個禮拜沒帶錢包出門 還是可以天天買滿買好回家(? 因此為了記錄手機消費跟各種紅利優惠 鴨鴨都會特別注意銀行的App好不好用! 像是介面設計就是會很在意的地方 很多銀行通常會為了要滿足不同客群 會推出很多App讓使用者下載 每次
Thumbnail
蘋果近日獲得一項與iPhone電池技術相關的新專利,將有助於提升手機的續航性能, 訴求可在不增加機身體積與重量的前提下,就能塞進更大容量的電池,被外媒視為是一大突破性的創新技術。 據外媒Gizchina 報導指出,該技術被命名為「Battery Cells with Tabs at Righ
Thumbnail
蘋果近日獲得一項與iPhone電池技術相關的新專利,將有助於提升手機的續航性能, 訴求可在不增加機身體積與重量的前提下,就能塞進更大容量的電池,被外媒視為是一大突破性的創新技術。 據外媒Gizchina 報導指出,該技術被命名為「Battery Cells with Tabs at Righ
Thumbnail
這篇文章探討瞭如何在iOS應用程式中客製化Alert,包括改變字體大小、內嵌連結以及讓Alert的高度隨著字數增長並提供scroll操作。同時使用SwiftUI進行客製化,並介紹瞭解決高度超出範圍後文字捲動與scrollView固定高度的方法。
Thumbnail
iOS 18 新增了原生 App 上鎖和隱藏功能,可以在打開應用程式增加驗證身分和隱藏敏感程式,即使將手機借給他人,對方沒有密碼也無法打開已上鎖的 App。
Thumbnail
iPhone 最新作業系統 iOS 18 控制中心大更新,預設有常用功能、音樂播放器、連線控制三個頁面,並允許使用者自訂頁面,可以直接在控制中心管理所需的功能、調整位置和選擇尺寸。
Thumbnail
iOS15推出了新的按鈕外觀設定功能,讓設定變得更加直觀。使用不同的圓角設定和圖片位置可以輕鬆創建不同風格的按鈕。另外,新的選取效果變化功能也讓按鈕設計更加靈活。本文將介紹這些新功能的使用方法和效果。
Thumbnail
作者 Only 系列文章,【一天一千字,進化每一次】手機容量大(ROM)是否智商稅? 選擇Apple的用戶是否值得買大容量手機?或是Google雲端費用是否更划算? 該不該直上頂規?手機選擇大銀幕,小銀幕,摺疊螢幕?本文從不同角度提供了選購手機及作業系統的參考建議。
Thumbnail
在這個大螢幕手機滿街跑的時代,小螢幕手機似乎成了稀有品,目前大多數螢幕尺寸就是6吋起跳,很難看到6吋以下的手機,並且許多廠商也不太願意繼續推出這類的產品,所以也就逐漸淡出了一般消費者的視野這篇,自然而然的小螢幕手機似乎就變成了小眾產品。 那......如果再次拿起小螢幕手機呢?
Thumbnail
避免網頁在不同瀏覽器中長不一樣。用 CSS Reset 來「重置」和統一瀏覽器預設樣式。
Thumbnail
由於iphone的「捷徑」功能實在太方便,連續幾篇都在探索還有什麼應用,這篇加上「動作按鈕」使用,讓捷徑功能更強大! 首先,預設的動作按鈕開啟相機功能需要進入UI畫面,然後再進行拍照,但有時畫面一閃而過,根本來不及捕捉。因此,今天分享一個簡化拍照動作的方法,只需一鍵完成拍照。 步驟如下: 先建