在方向改变时重新渲染网页的最佳方法是什么?

31

我有一个流体CSS布局,在更改方向时在iPhone上呈现不佳(刷新时看起来很好)。

我使用下面的代码在方向更改时刷新页面,可以正常工作,但这样做感觉有些不对。是否有任何方法可以实现此目的,而无需重新加载整个页面?这是一个移动站点,我真的不想强迫用户加载两次页面。

var supportsOrientationChange = "onorientationchange" in window,
    orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";

window.addEventListener(orientationEvent, function() {
    window.location.reload()
}, false);   

编辑:

在 iPhone 上进行测试时的两个主要问题是:

我有一个 div,它的宽度是 100%,带有右对齐的背景图像。当我从纵向模式改为横向模式时,body 宽度仍然保持在纵向模式下的状态,反之亦然。这更多是从横向模式到纵向模式的问题,因为页面太宽了,而且似乎会将图像呈现两次。


你已经使用CSS媒体查询了吗? - Pointy
@Pointy 没有媒体查询。无论是横向还是纵向,页面布局都是相同的(使用相同的CSS)。我已经更新了我的答案,以便更好地解释。 - theorise
你是只为 iPhone 设计页面吗?那 iPad、Android、WM7 等呢?如果不同宽度/尺寸的浏览器导致页面显示不佳,你需要检查布局。 - Ryan Ternier
@Ryan Ternier 是的,所有的手机都有这个问题。如果页面在方向改变后刷新,则布局会正常显示,因此这不是布局问题,而是当手机在方向转换后呈现页面时出现的问题。 - theorise
1
@theorise 有一个示例页面,这样我就可以测试你所描述的内容了吗? - RaphaelDDL
显示剩余2条评论
8个回答

28

假设你的CSS已经可以在各种不同大小的移动设备屏幕上愉快地呈现,那么你需要在模板的<head>中定义视口。

例如,这将把页面宽度设置为设备屏幕的宽度,并设置初始缩放比例为100%。初始缩放比例会在页面加载时应用,但在方向更改时不会应用。

<meta name="viewport" content="width=device-width, initial-scale=1.0">
通过在视口中添加最大比例=1.0参数,您将强制iPad/iPhone保持缩放级别并在方向更改时重新布局页面。这样做的缺点是它会禁用缩放。但是,优点是您可以使用媒体查询进行布局调整,以便根据当前方向呈现内容。您可以在此处阅读有关视口的更多信息:选择一个视口 现在让我们谈一下媒体查询。您应该将媒体查询放置在CSS文件底部,并按屏幕宽度从小到大的顺序排列。例如,从Html5BoilerPlate CSS示例中获取:
@media only screen and (min-width: 480px) {
  /* Style adjustments for viewports 480px and over go here */

}

@media only screen and (min-width: 768px) {
  /* Style adjustments for viewports 768px and over go here */

}

所以,所有常规样式都在此之上并且首先应用,然后如果屏幕宽度为480像素或更宽,则应用下一块样式;然后如果屏幕宽度为768像素或更宽,则应用最后一块样式。

通过将固定的缩放级别设置为1.0并使用媒体查询,您可以使您的站点在不使用JavaScript的情况下对屏幕大小和方向进行响应式调整。显然,您需要确保网站经过良好设计,以便用户不需要缩放。如果您的网站针对移动设备进行优化,则这不应该是一个问题。

请注意:其他非Safari移动浏览器可能会重新布局页面而不设置视口的最大比例尺。但是,此行为是不一致的,并且大多数开发人员似乎迎合苹果设备,即使实现比其他设备更糟糕。其他一些设备将保持缩放级别,并在方向改变时重新调整视口中心。但是,将所有设备的缩放级别固定为1.0都是可以的。


看起来您可以放弃上面建议的最大比例尺,如果您喜欢的话,可以通过一些JavaScript增强布局(无需重新加载页面)。查看这个关于通过JavaScript操纵视口的其他stackoverflow页面:使用JavaScript操作ViewPort - BenSwayne
è؟کوœ‰orientation:landscapeه’Œorientation:portraitهھ’ن½“وں¥è¯¢هڈ¯ن¾›ن½؟用م€‚ - Bob Aman
@BobAman 你说得对,我认为Html5Boilerblate CSS使用屏幕宽度是为了跨平台支持,包括桌面/笔记本电脑。例如,如果你在桌面机器上调整浏览器窗口大小为纵向形状(例如:在Windows 7机器上按Win+左键将屏幕左侧占用一半 - 常用于并排查看两个应用程序),那会怎么样呢?但是,如果你需要这些情况有不同的尺寸调整和方向调整,我想你可以扩展上述媒体查询来实现。 - BenSwayne
这也涉及设备尺寸支持更加普及的问题。 - Bob Aman
1
BenSwayne的回答:“通过在视口中添加maximum-scale = 1.0参数,您将强制iPad / iPhone保持缩放级别并在方向更改时重新布局页面。”似乎不再起作用。页面不会重新布局。自从这个线程被回答以来,iOS更新是否改变了什么? - Steve F
从Safari 7开始,这不再起作用。这里有一个演示。为了确保您看到它不起作用,请从横向模式开始使用iPad,加载页面,然后旋转。请注意,尽管一直使用flexbox,但页面并没有展开到完整的高度。 - Dan Dascalescu

14

2015 年更新

其他所有答案都不正确或已过时。以下是有效的解决方案:

  window.addEventListener('orientationchange', function () {
    var originalBodyStyle = getComputedStyle(document.body).getPropertyValue('display');
    document.body.style.display='none';
    setTimeout(function () {
      document.body.style.display = originalBodyStyle;
    }, 10);
  });

这段代码监听orientationchange事件,通过将body元素隐藏并在10毫秒后显示来强制重新布局。它不依赖于任何<meta>标签或媒体查询。

其他答案建议使用媒体查询,但是您已经在使用它们,因为您说“刷新后看起来很好”。

一些其他答案建议使用location.reload()。这非常低效,因为它将重新加载整个页面,包括图像等。特别是在移动设备上,您不想这样做。

其他回答建议添加<meta name="viewport" content="width=device-width, initial-scale=1.0">或类似的变体。但在Safari 7中,这种方法不再起作用。这里有一个演示。为了确保你看到它不起作用的情况,首先将iPad横向放置,加载页面,然后旋转。请注意,尽管一直使用flexbox,页面仍没有扩展到完整高度。

此页面相比,我们在生产中使用隐藏/显示主体技术。


2
这对我来说直接使用就可以了。非常好的解决方案。 - mags
好的,我之前过早地说了谢谢。对我来说它并没有起作用,而且我确实已经正确设置了元标签。问题中的div是一个动态渲染的幻灯片,它创建了一个带有内联样式定位绝对的子div。这是一些第三方的东西,所以当然我不能直接触碰代码本身(而且我也没有告诉我的雇主放弃它的选项),所以...请帮帮我,有人吗? - code-sushi
2
如果在10毫秒内触发了两次orientationchange事件,那么有可能会完全丢失body的display属性,并最终以display none结束... - Ashish Sajwan

3

使用一种方法在单个JavaScript堆栈帧中导致重绘和回流。不喜欢针对视口的特定答案,因为保持捏缩放等功能通常是无障碍性的要求。

$(window).on('orientationchange', function() {
    document.body.style.display='none';
    document.body.offsetHeight; //cause a reflow
    document.body.style.display='block'; //cause a repaint
}); 

或者没有使用jQuery的等效方法。
window.addEventListener('orientationchange', function () {
    document.body.style.display='none';
    document.body.offsetHeight; //cause a reflow
    document.body.style.display='block'; //cause a repaint
});

0

我曾经遇到过类似的问题,这是我如何使用jQuery解决它的方法。

在我的情况下,我的<nav>元素的样式没有正确地改变。我使用了kidkaos77修改过的代码,并结合$.get()只加载页面的某个特定部分:

$(window).bind("resize",function() {
    //on resize reload only nav & replace
    $.get("index.php" +  ' nav', function(data) {
        $("nav").replaceWith($(data).find("nav"));
    });
});

使用这种方法,您不必重新加载整个页面,但是您需要明确指定受方向更改影响的元素,而在您的情况下似乎只需要一个 div 元素。

这个解决方案能否在没有页面名称的情况下使用?只是 $.get(this + ' div.className', function(data) ... 吗?如果不能,为什么? - code-sushi

0
根据我的经验,最好的方法是这样的。
<meta name = "viewport" content = "user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width /">
<meta name="apple-mobile-web-app-capable" content="yes"/>

按照 @BenSwayne 的方法来做,在我看来,当你改变方向时,它不会自动缩放回初始比例。不知道为什么。


那也不行。这里有一个演示:https://idorecall.com/static/bug-demos/iPad-rotation-SO-21602276-1269037/。为了确保您看到它的不正常,从横向模式开始,在加载页面后旋转。请注意,尽管一直使用flexbox,但页面并没有展开到完整的高度。 - Dan Dascalescu

-1

另一个选择是从您的HTML元素(div、var、span等)中添加和删除CSS类。这样,您可以仅修改给您带来麻烦的元素,并且如果用户调整浏览器窗口大小,则还可以在非移动浏览器上调整内容。

以下是您需要的Javascript / JQuery代码:

// Code to run when page has finished loading
$(function() {
    // Add CSS-class to body depending on device platform using user agent string
    // You can add more validations here and/or separate Android from iPhone or add more customized classes like "landscape_iPad", "landscape_iPhone", etc.
   // You can also validate browser types and add classes like "background_IE" or "background_Chrome", etc
    if ((navigator.userAgent.indexOf("iPad") != -1)) {
        $("#background").addClass("landscape");
    } else if ((navigator.userAgent.indexOf("Android") != -1) || (navigator.userAgent.indexOf("iPhone") != -1) || 
    (navigator.userAgent.indexOf("iPhone") != -1)) {
        $("body").addClass("iPhone");
    }

    // Get the initial orientation on iOS devices
    if (!isNaN(window.orientation)) {
        var orientation = ($(window).width() < 980) ? "portrait" : "landscape";
        // Index php
        $("#background").addClass(orientation);
    } else {
        // Choose layout depending on viewport/window width
        var orientation = ($(window).width() < 980) ? "portrait" : "landscape";
        // Index php
        $("#background").addClass(orientation);
    }

    // Bind orientationChange (or viewport/window size changes)
    if (window.onorientationchange != undefined) {
        window.onorientationchange = function() {
            var orientation = ($(window).width() < 980) ? "portrait" : "landscape";
            // Index php
            $("#background").removeClass("portrait landscape").addClass(orientation);
        }
    } else {
        // Use landscape styling if it's wider than 980 pixels. 
        // This is for non mobile browsers, this way if the user resize the browser window, content will adjust too.
        $(window).bind('resize', function(){
            var orientation = ($(window).width() < 980) ? "portrait" : "landscape";
            // Index php
            $("#background").removeClass("portrait landscape").addClass(orientation);
        });
    }
});

这是样例元素“background”的CSS类:

#background.portrait {
    position:absolute;
    top:0px;
    left:0px;
    width:768px;
    height:946px;
    z-index:0;
    background:url(background.png) top center no-repeat;
}
#background.landscape {
    position:absolute;
    top:10px;
    left:20px;
    width:1024px;
    height:724px;
    z-index:0;
    background:url(background_landscape.png) top center no-repeat;
}

这样,您可以自定义横向和纵向的行为,并且您可以添加更多类别,例如:“landscape_iPhone”,“portrait_Android”或任何您需要控制每个特定设备页面呈现的类别。

此外,您无需重新加载页面,它会实时调整。

希望它能帮助您或其他人=),这使我能够使用相同的HTML但不同的CSS类别创建针对每个屏幕大小、移动品牌甚至浏览器类型的定制网站。


2
我赞赏你的热情,但是UserAgent嗅探正在被特性检测所取代。你永远不知道下一个平板电脑或浏览器何时会出现,可能会破坏你的UserAgent嗅探。此外,我不明白这样做有什么作用,而媒体查询则没有。我们不需要支持IE6的方向变化,因此我们可以自信地使用适当的当前技术。在我看来,JavaScript只应该用于填补浏览器之间的差距,即iOS Safari的视口处理与其他平板电脑浏览器之间的差异。 - BenSwayne

-1
尝试像这样做:

$(function(){

    changeOrientation(window.orientation == 0 ? "portrait" : "landscape");

    $('body').bind('orientationchange',function(event){
        changeOrientation(event.orientation)
    });

    function changeOrientation(ori){
        $("#orientation").removeClass('portrait landscape');
        $("#orientation").addClass(ori);
    }     
});

没有解释或HTML&CSS的#orientation元素,我怀疑意图与我的答案中身体的隐藏/显示相同。 - Dan Dascalescu

-2
$(window).bind('resize', function() { location.reload(); });

这段代码对我起作用。


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