用 css @media (orientation: ...) 可稳定处理横竖屏布局切换,无需 js 监听旋转事件;现代浏览器支持成熟,基于 viewport 宽高比计算,不依赖传感器且抗干扰;js 场景优先用 matchmedia,fallback 用节流的 resize。

直接用 @media (orientation: portrait) 和 @media (orientation: landscape) 就能解决大部分横竖屏布局切换问题,不需要 JS 监听旋转事件——除非你要做动画、上报日志或触发非样式逻辑。
用 CSS 媒体查询判断横竖屏最稳
现代浏览器对 @media (orientation: ...) 的支持已非常成熟(iOS Safari 12.2+、Chrome 74+、Firefox 69+),它基于 viewport 宽高比实时计算,不依赖设备物理传感器,也不会被键盘弹出、缩放等干扰。
- 竖屏时
@media (orientation: portrait)生效,哪怕你把 iPad 横着拿但浏览器窗口被限制为窄高比 - 横屏时
@media (orientation: landscape)生效,桌面浏览器缩放窗口也能触发,行为可预期 - 不要混用
window.orientation或screen.orientation.type—— 前者已被 Chrome 95+/Safari 16.4+ 彻底移除,后者无法监听变化且需用户手势才能启用 - 示例:表单在竖屏堆叠、横屏并排
.form-row {
display: flex;
flex-direction: column;
}
@media (orientation: landscape) {
.form-row {
flex-direction: row;
}
.form-row > * {
flex: 1;
margin-right: 8px;
}
.form-row > *:last-child {
margin-right: 0;
}
}
resize 事件要节流,否则 iOS 上会疯掉
如果真得用 JS 响应方向变化(比如重绘 Canvas、调整 video 尺寸),resize 是唯一兼容性全覆盖的 fallback 方式,但它在 iOS 上旋转时高频触发(尤其带滚动或键盘),不节流会卡死主线程。
- 别用
debounce库——简单用setTimeout+ 标记就够了 - 延迟 120ms 足够过滤抖动,又不会明显感知延迟
- 注意:
resize在桌面浏览器缩放时也会触发,和 orientation 查询语义不同,要按需区分 - 错误写法:
window.addEventListener('resize', handle)(无节流) - 正确写法:
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
if (window.innerWidth > window.innerHeight) {
// 横屏逻辑
} else {
// 竖屏逻辑
}
}, 120);
});
matchMedia 是 JS 场景下的首选替代方案
比起自己比宽高,window.matchMedia 更精准、语义清晰、且只在真正方向切换时触发,是目前 JS 侧最推荐的方式。
- 首次调用
mql.matches就能拿到当前状态,不用等事件 - 监听用
mql.addEventListener('change', ...),旧版 Safari(≤12.1)才需回退到mql.addListener - 注意:它不响应 viewport 缩放,只响应 orientation 变化,和 CSS 媒体查询行为一致
- 示例:
const mql = window.matchMedia('(orientation: landscape)');
function handleOrientationChange(e) {
if (e.matches) {
document.body.classList.add('landscape');
} else {
document.body.classList.remove('landscape');
}
}
mql.addEventListener('change', handleOrientationChange);
handleOrientationChange(mql); // 立即执行一次
Flex/Grid 子项在方向切换时容易撑破容器
横竖屏切换后,常见问题是 Flex 子项突然换行、Grid 列宽错乱、或内容溢出——根源往往不是媒体查询没写,而是盒子模型或最小尺寸约束没处理好。
-
min-width: 0必加:Flex 子项含长文本或 URL 时,默认不收缩,加这句才能让flex: 1生效 -
box-sizing: border-box全局重置必须写:否则 padding/border 会让 width 计算失准,横屏时宽度叠加超限 - 避免在媒体查询里只改
flex-direction却忽略flex-wrap:竖屏设flex-wrap: wrap,横屏可能得关掉 - Grid 布局中,
grid-template-columns: 1fr 1fr在窄屏下会把两列压成极细,建议搭配minmax(320px, 1fr)
真正难的不是写对媒体查询,而是理解 orientation 媒体查询基于 viewport 而非设备——它在桌面浏览器缩放时也生效,这点常被忽略,导致测试时以为“没反应”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











