html全局属性在移动端不直接适配,需针对浏览器行为差异处理:viewport必须硬编码于head顶部;contenteditable需配合focus时机与scrollintoview修正光标定位;draggable须严格对齐effectallowed/dropeffect且setdragimage仅支持img/canvas;tabindex="-1"仅限主动聚焦,非隐藏开关。

移动端开发中,HTML全局属性本身不“适配”,真正需要适配的是它们在不同浏览器、系统版本、渲染上下文下的行为表现。直接套用桌面端写法,90% 会出问题——比如 contenteditable 光标被键盘盖住、draggable 在 Safari 里静默失败、tabindex 打乱焦点流。
viewport 标签没写对,其他全是白搭
所有全局属性的可靠运行,都依赖 viewport 正确启用移动渲染模式。它不是可选项,而是开关。
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">必须硬编码在最顶部,早于任何 CSS/JS;动态插入或 SSR 后补,iOS Safari 直接忽略 - 只写
width=device-width不够:iOS 会先以 980px 渲染再缩放,导致contenteditable的getBoundingClientRect()坐标错位、dragover触发区域偏移 - 禁用
user-scalable=no:不仅 WCAG 不合规,还会让 iOS 13+ 降权提示,且横屏时 fixed 元素卡死在物理底部,而非可视区域底部 - 别写
width=375:iPhone SE 和 Pixel 7 的 “375” 对应不同 DPR 下的逻辑宽度,横屏时直接崩
contenteditable 光标定位失效,关键在滚动时机和目标
focus 后光标被软键盘盖住,不是 scrollIntoView 没用,而是你滚错了对象、也滚错了时间。
- 不要对
contenteditable元素自身调scrollIntoView():它可能还没完成重排,getBoundingClientRect()返回{ height: 0 } - 必须在
focus回调里,先用getSelection().getRangeAt(0)获取当前 Range,再取range.getBoundingClientRect().bottom,对比visualViewport?.height判断是否被遮挡 - iOS 需微任务等待渲染:设置
contenteditable="true"后立即读getSelection()是空的,要加setTimeout(() => { /* 构造 Range */ }, 0) - Android fallback 用
window.innerHeight + document.documentElement.scrollTop算视口底边,因为visualViewport支持弱
draggable 在 Safari/Firefox 上静默失败的三个硬性条件
拖拽事件被拦截、光标变禁止图标、drop 无响应?大概率是 effectAllowed 和 dropEffect 没对齐,或 setDragImage 传了非法图像。
-
dragstart中必须显式设e.dataTransfer.effectAllowed = 'move'(不能靠默认值),Safari 会严格比对这个值和dragover里的e.dataTransfer.dropEffect -
dragover回调里必须同步写e.dataTransfer.dropEffect = 'move',类型完全一致;用'all'或'uninitialized'Safari 不识别 -
setDragImage()在 Firefox 要求图像已加载且宽高 > 0,在 Safari 只接受<img>或<canvas></canvas>实例;传document.body或未渲染的 div,Firefox 静默忽略 - 避免用
dragenter/dragleave控制高亮:Safari/Firefox 触发太敏感,频繁进出导致样式闪烁;改用dragover+requestAnimationFrame节流判断“真正悬停”
tabindex="-1" 被误用为隐藏开关,结果焦点消失
tabindex="-1" 不是 hidden,它的唯一合法用途是:让元素不可 Tab 进入,但能被 .focus() 主动聚焦。
- 模态框打开后聚焦第一个按钮,必须确保该按钮已挂载到 DOM,且没有
display: none或visibility: hidden;否则.focus()静默失败 - 聚焦后若元素被
overflow: hidden或position: absolute遮挡,需立刻跟element.scrollIntoView({ block: 'nearest' }),否则用户看不见焦点在哪 - 别给整个
tabpanel容器设tabindex="-1":.focus() 落在空容器上,Tab 键仍会跳到页面其他地方 -
tabindex="0"只加在需键盘进入的非原生可聚焦元素上,如<div role="button">;给纯文本加,屏幕阅读器多读一遍“无意义 div”<p>这些属性的行为差异,不是 bug,而是各浏览器对规范实现的节奏不同。最危险的,是把桌面端验证过的写法直接搬到移动端——比如以为 <code>scrollIntoView能解决一切 focus 定位,或相信tabindex="-1"会让元素“彻底消失”。真实世界里,它们只在特定时机、对特定目标、配合特定 fallback 才有效。











