CSS媒体查询 - 软键盘破坏了CSS方向规则 - 是否有替代解决方案?

88

我正在使用多个平板设备 - 包括安卓设备。目前,所有平板设备都有以下分辨率变化:

  • 1280 x 800
  • 1280 x 768
  • 1024 x 768 (iPad明显不适用)- iPad没有这个问题

应用基于设备方向的样式的最简单方法是使用媒体查询的方向,使用以下语法。

@media all and (orientation:portrait)
{
  /* My portrait based CSS here */
}

@media all and (orientation:landscape)
{
  /* My landscape based CSS here */
}
这在所有平板设备上都能正常工作。但问题是,当设备处于纵向模式并且用户点击任何输入字段(例如搜索框)时,软键盘弹出,这会降低网页的可见区域并迫使其基于横向 CSS 进行渲染。在 Android 平板设备上,这取决于键盘的高度。因此,最终网页看起来会出现问题。因此,我无法使用 CSS3 的方向媒体查询根据方向应用样式(除非有更好的媒体查询来针对方向)。这里有一个代码片段http://jsfiddle.net/hossain/S5nYP/5/,可以模拟这个问题 - 用完整测试页面进行设备测试-http://jsfiddle.net/S5nYP/embedded/result/

以下是从演示页面中获取的行为截图。 enter image description here

那么,有没有其他方法解决这个问题呢?如果本地 CSS 解决方案不起作用,我可以尝试 JavaScript 解决方案。

我在http://davidbcalhoun.com/2010/dealing-with-device-orientation上找到了一段代码片段,建议在 上添加类,并根据此来定位。例如:

<html class="landscape">
  <body>
    <h1 class="landscape-only">Element Heading - Landscape</h1>
    <h1 class="portrait-only">Element Heading - Portrait</h1>
    <!-- .... more... ->

# CSS
.landscape .landscape-only { display:block; }
.landspace .portrait-only  { display:none; }
.portrait .portrait-only   { display:block; }
.portrait .landscape-only  { display:none; }

你们对这个有什么想法?你们有更好的解决方案吗?


刚刚发现,iPad 没有这个问题。只有 Android 操作系统(Honeycomb 平板电脑)和手机才有这个问题。 - Hossain Khan
我在我的移动网站上遇到了类似的问题。经过在几个安卓设备上的测试,似乎并不局限于任何特定的操作系统或移动/平板设备。看起来这主要是摩托罗拉和三星设备的问题。我们无法在我们的HTC手机上复制这个问题。 - TJ Kirchner
1
这个问题主要出现在安卓设备上,移动/平板电脑 - 唯一的复现方法是安装具有较大高度的自定义键盘,这样当键盘显示时,webview 的可用高度小于可用宽度。只有这样,webview 才会触发方向更改。例如,在屏幕截图中,如果我可以禁用键盘顶部的建议层,则此问题将不存在,因为 webview 高度将大于宽度。但是,无论如何,我还没有找到任何优雅且有效的解决方案。 - Hossain Khan
1
刚刚意识到 iOS 7.0.0 的全屏 WebApp 也有这种行为。 - Alexandre
很遗憾。我无法有效地解决这个问题,因此将其标记为“已知限制”。 - Hossain Khan
1
请查看此答案以获取解决方案:https://dev59.com/4WQn5IYBdhLWcg3wamjO - John Conde
12个回答

111

我知道这已经晚了几年,但我找到了一个很好的解决方案。

对于横向媒体:

@media screen and (min-aspect-ratio: 13/9) { /* landscape styles here */}

对于纵向媒体:

@media screen and (max-aspect-ratio: 13/9) { /* portrait styles here */}
完整的解决方案以及为什么它有效的说明可以在这里找到:Michael Barret - Android browser challenges
编辑:此链接现已过期,但可以在互联网档案馆中找到快照:Michael Barret - Android browser challenges

4
这解决了问题,但对于我的其他设备(主要是笔记本电脑和电脑)却带来了更多问题。 - Stephan Bijzitter
1
不幸的是,这个解决方案在所有移动设备上都无法工作。 - Diogo Cardoso
4
这种解决方案比较简单,可能不适用于某些设备。比如说,打开软键盘时,我的手机显示的宽高比约为17.7/9,而在许多其他设备上,横向宽高比要小得多。 - raacer
3
完整解决方案链接已损坏。 - Sparkmorry
1
这依赖于移动设备当前的最大宽度,而随着技术的改进,这些将会发生变化。所以我们应该改变所有媒体查询的情况吗?我认为这是一个不好的想法。 - QMaster
显示剩余4条评论

29
问题在于计算方向的方式:

http://www.w3.org/TR/css3-mediaqueries/#orientation

“方向”媒体特性在“高度”媒体特性的值大于或等于“宽度”媒体特性的值时为“纵向”,否则为“横向”。由于高度/宽度是在可见视口上计算的,软键盘显然会导致方向翻转,因为视口宽度小于高度。一个解决方法就是仅基于“宽度”使用您的媒体查询。这使其在不考虑方向的情况下更加灵活,而且宽度/高度比方向更广泛地得到支持。如果您想考虑宽度而不是方向,我将以iPhone为例:
@media screen and (max-width: 320px) {
  /* rules applying to portrait */
}

@media screen and (max-width: 480px) {
  /* rules applying to landscape */
}

这种方法比方向更灵活,因为查询不限于支持方向的设备/用户代理,更不用说方向相对于宽度提供的信息非常少
当然,如果你真的需要知道方向,似乎最好的选择是最初设置类并仅使用该类。

1
我了解浏览器如何计算横向和纵向布局。但是,我需要另一种更好的处理方式。另一个有趣的事情是,当iPad键盘弹出时,它不会触发方向更改。我不知道为什么该死的安卓要实现这种方式。 - Hossain Khan
@HossainKhan @media all and (max-device-width:NNNpx){} 或者 @media all and (min-device-width:NNNpx){} 或者 @media all and (device-width:NNNpx){} - RaphaelDDL
我更新了我的回答。设备宽度应该考虑方向,这样当您的方向改变时,宽度应相应更新。 - scurker
查看这篇帖子,了解如何使用该技术来定义您的视网膜图像。 - Jason
1
最终通过移除 and (orientation:portrait) 并仅应用最大和最小宽度解决了我的问题。!!! - Libin
显示剩余2条评论

16

另一种选择可能是使用device-aspect-ratio,它保持不变。 但是,在Webkit中,旋转设备不会触发使用此查询的CSS规则更新,即使JavaScript测试返回新纵横比的真值。这显然是由于一个错误,但我找不到错误报告。使用orientation{min,max}-device-aspect-ratio的组合似乎可以正常工作:

@media screen and (max-device-aspect-ratio: 1/1) and (orientation: portrait)
@media screen and (min-device-aspect-ratio: 1/1) and (orientation: landscape)

orientation的使用会按预期触发更新,而device-aspect-ratio的使用会限制更新仅在实际方向更改时才会发生。


很遗憾,此功能现已被弃用 https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media/device-aspect-ratio - Eugene Gluhotorenko
到了2020年中期,尽管已经被弃用,但这仍然有效...太棒了,这是我在所有情况下唯一真正起作用的解决方案! - qraqatit

6

我仔细尝试了上述选项,但都不能完全解决我的问题。因此,我开始使用screen.availHeight,这可以提供一致的高度结果,避免键盘显示问题。

// Avoiding the keyboard in Android causing the portrait orientation to change to landscape. 
// Not an issue in IOS. Can use window.orientation.
var currentOrientation = function() {
  // Use screen.availHeight as screen height doesn't change when keyboard displays.  
  if(screen.availHeight > screen.availWidth){
    $("html").addClass('portrait').removeClass('landscape'); 
  } else {
    $("html").addClass('landscape').removeClass('portrait'); 
  }
}

// Set orientation on initiliasation
currentOrientation();
// Reset orientation each time window is resized. Keyboard opening, or change in orientation triggers this.
$(window).on("resize", currentOrientation);

所以我已经进行了一些测试,发现这并不是那么简单。Android 设备需要使用 screen.availHeight 和 screen.availWidth。但是 IOS 在使用这些属性时会出现错误,因此应该在 IOS 上使用 window.orientation。桌面端应该使用 window.innerHeight 和 window.innerWidth,因为你需要的是窗口尺寸而不是屏幕尺寸。 - Robby Jennings
我一直在为上面的代码苦苦挣扎。太过于棘手了。我不支持桌面版应用,所以我使用window.orientation作为Android和IOS的神奇解决方案。现在工作得很好。变量orientation的值是Math.abs(window.orientation) === 90 ? 'landscape' : 'portrait'; - Robby Jennings

5

对我来说,这个方法很有效:

  @media screen and (max-device-aspect-ratio: 1/1), (max-aspect-ratio: 1/1){
        /*Portrait Mode*/
};

虽然 `max-aspect-ratio` 可以通过调整窗口大小触发纵向模式(适用于 PC 和其他仅支持横向显示的设备),但是 `max-device-aspect-ratio` 则可帮助智能手机或其他具有可变方向的设备。而且,由于我没有为横向模式指定特定的设置,即使 `max-aspect-ratio` 向系统报告了 >1 的值,如果 Android 设备以该方式定位,则 `max-device-aspect-ratio` 仍将触发纵向模式。
`1/1` 部分基本上意味着,如果比例 <= 1,则进入纵向模式,否则进入横向模式。

2
键盘弹出时怎么办? - adi518
当移动设备上弹出虚拟键盘时,什么也不会改变,因为设备的纵横比(max-device-aspect-ratio)只有在实际旋转设备或故意从纵向模式更改为横向模式及其反之后才会改变。 - helveciofneto
是的,最终我自己实现并测试了它。我正在围绕此开发一个包:https://www.npmjs.com/package/mobile-orientation - adi518
回来询问另一件事:“(max-aspect-ratio:1/1)”。看起来我们只需要这个来检测桌面上的肖像画? - adi518
没错。1/1 的作用是确保完美的正方形仍会触发纵向模式(在允许窗口调整大小的系统中,如桌面上的浏览器窗口)。如果没有它,在这个精确比例下会发生奇怪的事情。 - helveciofneto
显示剩余4条评论

1
我已经阅读了上面的几个答案,但没有一个完全符合我的要求,即尽可能地模仿iPhone的功能。以下是目前在生产中对我有效的内容。
它通过将设备视口的窗口宽度和高度分配给数据属性以供将来检查来工作。由于手机在拉起键盘时不会调整宽度,只有高度,因此检查比例和宽度与原始宽度相比可以告诉您键盘是否弹出。我还没有找到这种方法失败的用例,但我肯定会找到。如果你们找到了,请告诉我。
我正在使用一些全局变量,例如InitialRun和MobileOrientation,用于js切换,我还使用html5数据属性在DOM中存储信息以供css操作。
    var InitialRun = true; // After the initial bootstrapping, this is set to false;
    var MobileOrientation = 'desktop';
    function checkOrientation(winW, winH){ // winW and winH are the window's width and hieght, respectively
        if (InitialRun){
            if (winW > winH){
                $('body').attr('data-height',winW);
                $('body').attr('data-width',winH);
                MobileOrientation = 'landscape';    
                $('body').attr('data-orientation','landscape'); 
            }
            else{
                $('body').attr('data-height',winH);
                $('body').attr('data-width',winW);
                MobileOrientation = 'portrait';
                $('body').attr('data-orientation','portrait');
            }
        }
        else{
            if (winW > winH && winW != $('body').data('width')){
                MobileOrientation = 'landscape';    
                $('body').hdata('orientation','landscape'); //TODO:uncomment
                $('body, #wrapper').css({'height':"100%",'overflow-y':'hidden'});
                //$('body').attr('data-orientation','portrait');
            }
            else{
                MobileOrientation = 'portrait';
                $('body').attr('data-orientation','portrait');
                $('body, #wrapper').css({'height':$('body').data('height'),'overflow-y':'auto'});
            }
        }

    }

1
我用了一个简单的技巧来解决这个问题。
@media (max-width: 896px) and (min-aspect-ratio: 13/9){/* Landscape content*/}

我发现max-width: 896px适用于所有移动设备。尝试这个解决方案,它有效!


你救了我的一天:),但是当页面从横向切换到纵向时,页面的缩放出现了问题,该如何处理? - Amir Farahani
这取决于移动设备当前的最大宽度,随着技术的不断改进,这些值将会发生变化。那么我们是否应该更改所有媒体查询的情况呢?我认为这是一个不好的想法。 - QMaster

0

我在iPad上遇到了完全相同的问题。
即在肖像模式下出现软键盘时,设备检测到了横向方向,并应用了横向样式到屏幕上,导致视图混乱。

设备规格

  • 设备:iPad Mini 4
  • 操作系统:iOS 11.2.6
  • 屏幕分辨率:1024 x 768

不幸的是,对我来说,这个解决方案没有起作用。

@media screen and (min-aspect-ratio: 13/9) { /* landscape styles here */}
@media screen and (max-aspect-ratio: 13/9) { /* portrait styles here */}

所以,我做的是,为我的纵向模式编写了一个媒体查询,就像这样。

@media only screen and (min-width : 300px) and (max-width : 768px) and (orientation : landscape) {
/* portrait styles, when softkeyboard appears, goes here */
}

当软键盘出现时,屏幕高度会减小,但屏幕宽度仍保持为768像素,这导致媒体查询将屏幕检测为横向方向。因此,上述解决方法可以解决该问题。

0

这对我来说非常可靠。我正在使用http://detectmobilebrowsers.com/作为jQuery扩展来检测$.browser.mobile。

if ($.browser.mobile) {
  var newOrientationPortrait = true;

//Set orientation based on height/width by default
  if ($(window).width() < $(window).height())
      newOrientationPortrait = true;
  else
      newOrientationPortrait = false;

//Overwrite orientation if mobile browser supports window.orientation
if (window.orientation != null)
    if (window.orientation === 0)
        newOrientationPortrait = true;
    else
        newOrientationPortrait = false;

这是我根据设备方向修改视口的代码。这个函数只在移动设备上调用,甚至不包括平板电脑。这使得我可以轻松地为400像素和800像素(纵向和横向)编写CSS样式。
_onOrientationChange = function() {
    if (_orientationPortrait) 
        $("meta[name=viewport]").attr("content", "width=400,user-scalable=no");
    else 
        $("meta[name=viewport]").attr("content", "width=800");
}

由于我的需求的特殊性,我无法仅通过CSS媒体查询来实现,因为DOM本身需要根据方向进行更改。不幸的是,在我的工作中,商业人士在未经开发人员咨询的情况下编写软件需求。

0
想法:从媒体查询中删除肖像参数,只留下最小宽度。在该媒体查询中进行常规的肖像模式样式设置。然后,创建另一个媒体查询,用于横向模式,具有足够高的最小高度,以便当键盘弹出时浏览器不使用该查询。您必须尝试确定这个“足够高的最小高度”,但由于大多数(如果不是全部)横向模式的高度都比键盘弹出时的高度要大,因此这不应该太难。此外,您可以将“最小宽度”添加到横向查询中,该宽度大于肖像视图中大多数智能手机的宽度(即约为~400px),以限制查询范围。
以下是另一种(脆弱的)解决方案: - 在您的HTML中添加一个空的任意元素,在肖像模式以外的任何地方都将具有“display:none”。 - 创建一个jquery或javascript监听器以获取input:focus。当单击输入时,您的javascript检查任意元素的显示属性。如果它返回肖像模式期间设置的“block”或其他内容,则可以使用JS覆盖您的样式到肖像模式查询。相反,您将具有input:blur监听器来清除您硬编码的元素的style.cssText属性。

我正在尝试自己实现这个功能-如果我得到了可靠且易于管理的代码,我会发布它(我的第一个解决方案似乎是最合理的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接