十二、为移动设备设计应用

我们已经在很短的时间内开发了一个相当完整的应用。我们的应用在桌面浏览器上运行良好,实际上在移动设备上相当有用(我们将很快详细说明这一点)。这在很大程度上是由于微软所做的工作以及他们提供的新应用模板。这些模板基于一套新的标准:HTML5 和 CSS3。

本章将重点介绍 HTML5 和 CSS3,大多数移动浏览器对这些新标准的支持,以及使用这些标准设计下一代移动 web 应用的能力。

HTML5

HTML5 是其前身的一个明显的转变。HTML5 不是关注标识数据结构的标记,而是专注于为内容提供意义的标记的语义语言。

除了这些语义标签之外,HTML5 还引入了对嵌入式视频和音频、浏览器本地数据库存储、绘图画布、表单控件、网络套接字以及足以填满整本书的其他功能——实际上有几个。有了新功能,HTML5 还减少了我们必须编写的标记量,以生成符合标准的文档。

HTML5 的内容肯定比我们在一章中所能涵盖的更多,所以我们现在将重点关注对我们的应用最重要的特性:标记更改、语义标签、自定义数据属性和新的表单控件。我们还将介绍 HTML5 提供的本地存储和地理定位服务,因为它们可以用来大大增强移动应用的体验。

标记更改

让我们通过查看一个传统的最低限度兼容的 XHTML 1.1 文档来开始对这些标记变化的检查。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>BrewHow</title>
        <link rel="stylesheet" href="styles.css" type="text/css">
    </head>
    <body>
    </body>
</html>

马上,你就能看出这不是大多数人会用手打出的东西。HTML4 和 XHTML 1.1 给作者带来了很大的遵从负担。这是不幸的,因为解析器可以推断出合规性所涉及的标记的很大一部分。

在这方面,HTML5 旨在减少生成符合标准的文档所需的工作量。

文档类型标签

我们在每个超文本标记语言文档的开始看到的 DOCTYPE标签实际上并不是一个超文本标记语言标签。HTML 最初是与标准通用标记语言(SGML)一起开发的,这是一个旨在使用标记语言来描述标记语言的国际标准化组织标准。HTML4 基于 SGML,需要声明一个DOCTYPE

DOCTYPE声明包含几条信息,它们共同组成了文件类型定义 ( DTD ) 。DTD 非常正式,严格定义了标题下的文档。由于 HTML4 是 SGML 的真正衍生,所以DOCTYPE头是一个必需的标记——缺点和全部。

由于 HTML5 不是基于 SGML 的,因此没有相关的开销,我们可以缩短声明。所以,在 HTML5 中:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

变成:

<!DOCTYPE html>

当浏览器看到这个最小的DOCTYPE声明时,它将尝试使用它拥有的最新引擎来解析和呈现文档。

需要注意的是,HTML5 不区分大小写。DOCTYPEDocTypedoctypeDoCtYpE待遇都一样。无论您选择使用哪种格式,只要确保您在整个文档中是一致的——为了您自己,而不是解析器。

字符集

XML 和 HTML 4.0 的默认字符集是 Unicode,特别是 ISO 10646。这不是必须强制执行的事情。相反,这个标准的存在是为了指示所有的 XML 解析器和网络浏览器,它们必须像在内部使用 Unicode 一样工作。当我们想和其他人玩得很好时,识别编写文档的字符集对我们和文档的消费者都有好处,这样客户就可以将我们的字符集映射到 Unicode。

在 HTML4 文档中,使用meta标记来指定字符集,以识别文档的Content-Type:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

鉴于文档的类型在很大程度上是由DOCTYPE标签标识的,HTML5 为meta标签引入了一个新的charset属性,使得我们的字符集的声明更加简洁:

<meta charset="utf-8" />

类型属性

当声明要加载脚本、样式或其他外部媒体时,我们传统上必须通过type属性提供外部媒体的内容类型。对于 JavaScript,我们必须将类型标识为text/javascript。对于 CSS,我们必须提供一个内容类型text/css

<link rel="stylesheet" href="styles.css" type="text/css">

假设浏览器可以推断出这些信息,那么在 HTML5 中type属性已经变成可选的,这样我们用来加载样式表的link标记现在可以写成如下形式:

<link rel="stylesheet" href="styles.css" />

类型

想了解更多?

在 HTML5 中有几个额外的属性修改,你可能不会每天遇到。例如,script标签的async属性可以指示浏览器加载并立即执行外部 JavaScript,而无需等待页面完成。我鼓励你研究 HTML5 标准,目前在http://www.w3.org/TR/html5/处于候选人推荐状态。

把它放在一起,我们的样板 HTML5 模板比本节开始时呈现的 HTML4 标记更清晰,更容易可视化解析。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>BrewHow</title>
        <link rel="stylesheet" href="styles.css" />
    </head>
    <body>
    </body>
</html>

Visual Studio 2012 支持

Visual Studio 2012 知道所有这些新的标记更改,并在我们的 BrewHow 应用使用的默认模板中为它们提供支持。

如果我们在项目中打开_Layout.cshtml文件,您会看到文件的前四行如下:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />

似乎 Visual Studio 已经完成了简化标记的工作。让我们了解一下 HTML5 的语义标签,看看 Visual Studio 是否会像支持更简洁的标记一样为我们提供同样的支持。

语义标签

正如在本章介绍中提到的,语义标签允许我们将上下文应用到我们的内容中。使用一个简单的例子,可以通过使用div标签在 HTML4 中声明一个页脚。

<div id="footer"></div>

在 HTML5 中,footer是一个有效的标记,因此,我们的页脚声明现在为标记提供了意义。

<footer></footer>

HTML5 中的语义标签不是盲目选择的。相反,人们做了很多研究来查看网络上的内容,从这些内容中,最常用的类名和标识被编译并折叠成新的语义标签。

如果我们查看项目中已经存在的 HTML5 标记,可能会更容易,而不是提供 HTML5 中引入的所有标记的列表。我们的_Layout.cshtml模板已经为我们提供了几个 HTML5 语义标签,这些标签已经在下面的代码片段中突出显示:

<body>
 <header>

        <!-- snip -->
 <section id="login">
                    @Html.Partial("_LoginPartial")
                </section>
 <nav>
                    <ul id="menu">
                        <li>@Html.ActionLink("Recipes", "Index", "Recipe")</li>
                        <!-- snip -->
                    </ul>
                </nav>
            </div>
        </div>
    </header>
    <div id="body">
        <!-- snip -->
    </div>
 <footer>
        <!-- snip -->
    </footer>
        <!-- snip -->
    </body>

看来,Visual Studio 又一次为我们简化了流程。让我们更详细地检查这些标签中的每一个,看看我们是否需要进行任何其他标记更改。

文章标签

article标签标识页面内的独立内容。你可以使用标签来识别一篇博客文章,或者,在我们的应用中,一个食谱。使用article标签时遵循的一般经验法则是询问article标签中的内容本身是否有意义。如果是的话,一个article标签是合适的。我们的模板中没有article标签,但它仍然是 HTML5 环境中的一个重要标签,我们将在本书的剩余部分中利用它。

标题标签

标签正是它的名字所暗示的。它用于识别分段内容的标题。在这种情况下,分段内容并不特定于下一节中讨论的section标签。相反,它意味着页面的任何逻辑分组,如节、文章或文档本身,都可以包含一个标题来标识其中包含的内容。

给定模板中的标题范围,我们可以确定这个标题指的是整个页面的内容。

截面标记

部分是一组内容,可以包含也可以不包含页眉和页脚。它用于将较大的内容分组分解成较小的块。

_Layout.cshtml中的代码中,section标签用于标识特定于登录功能的标题部分和特定于页面一般内容的页面部分。

你也可以在一篇文章中加上section标签,把它分成几章。

导航标签

nav标签通常用于指示站点级别的导航。屏幕阅读器和其他浏览器可能实际上省略了这个标签的初始加载,以向用户提供对首先加载的页面的主要内容的访问。

我们的_Layout.cshtml文件包含一个nav部分,该部分包含配方列表、样式列表和用户库的链接。

页脚标记

正如标签描述中的所指出的,这些标签相互独立,不需要将任何标签嵌套在另一个标签中。我们可以将headerfooter标签应用到一个article标签,一个section标签,或者像在我们的_Layout.cshtml文件中所做的那样,应用到整个文档。同样,物品可以包含儿童物品,并且每个物品可以被分成多个部分。语义标记背后的整个思想是为我们的内容提供意义,然后可能被其他人解释。

其他 HTML5 标签包括addressasidedatefigurefigcaptionhgroupmarktime。关于 HTML5 标签的完整列表,我推荐http://www.w3.org/TR/html5/的 HTML5 候选推荐。

Visual Studio 中的新模板在使用新的 HTML5 标记方面做得非常好,因此我们应该只做一些修改。我们将使用配方详细信息视图来检查这些修改。

修改配方细节

在我们开始进行修改之前,让我们先来看一下食谱细节是如何在网络浏览器中呈现给用户的。

Modifying recipe details

以下是标记中用于显示配方细节的部分:

<h2>Details</h2>
<fieldset>
    <legend>Recipe Details</legend>

<!--Fields describing the recipe -->
</fieldset>

如果现有标记通过 HTML5 大纲视图运行,例如位于http://gsnedders.html5.org/outliner/的大纲视图,它将生成以下大纲:

Modifying recipe details

为了使当前的标记有意义,我们可以改变一些事情。首先,有一个h2标签,用于将下面的信息标识为详细信息。这个似乎严重不足;它当然不是对内容的描述。其次,我们的食谱细节内容在我们网站的上下文之外是有意义的,这是一个值得标签的描述。

了解了如何更好地将标记应用于内容,我们可以将详细视图调整为如下所示:

<article id="recipe-detail">
    <header>
        <h2>Recipe for @Model.Name</h2>
    </header>
    <fieldset>
        <legend>Recipe Details</legend>
        <!--Fields describing the recipe -->
    </fieldset>
</article>

对标记的这些更改对我们的应用在用户浏览器中的显示方式没有影响,除了在标题中显示配方名称的更改。这些变化确实对标记的解析和解释产生了显著的影响。如果我们通过同一个解析器运行新的标记,我们会得到以下概要:

Modifying recipe details

第一部分是页面本身。页面中包含登录部分、nav标签中包含的菜单和内容部分。内容部分是我们的食谱文章——第三个(也是新的)无标题部分。如果每个部分都被命名,理想情况下,你应该只使用部分标签,如果你可以应用一个标题,我们会有一个轮廓分明的页面。我们将在应用中的其他表单页面上应用适当的标题。要查看这些变化,您可以查阅本书附带的代码。

除了这些新的语义标签之外,HTML5 还提供了通过一组新的属性(统称为自定义数据属性)将私有数据与任何 HTML 标签相关联的能力。

自定义数据属性

自定义数据属性有时被称为属性,这是有充分理由的。假设属性以data-开头,并且-后面的任何内容都比单个字符长,则该属性被归类为自定义数据属性,并将被视为自定义数据属性。当我们实现我们的食谱库时,我们实际上使用了其中一个data-*属性。

浏览器或检索标记的任何其他用户代理完全忽略自定义数据属性。它们对页面解析或页面呈现没有影响。它将被视为根本不存在。同样,页面的用户或任何二手消费者也应该忽略这些属性。

规范实际上将这些属性称为私有属性。这并不意味着它们不会出现在浏览器的源代码中,或者在传输到浏览器的过程中被神秘地从源代码中移除。如果用户查看页面的来源,或者如果外部代理选择从我们的应用中的页面解析data-*属性,他们当然可以这样做。在这种情况下,私有意味着意图,而不是实际实现。我们不应该在这些属性中存储敏感信息。

我们可以存储在这些属性中的东西是标签没有现有属性的东西。我们当然不会在这里存储元素标题,因为 HTML 标准规定所有标签都支持title属性。但是,我们可以为字段存储一些初始值,或者为标签或容器指定角色。

<a href="#" data-initiallink="{oldlink}">Changing Link</a>

我们对这些属性的使用实际上是无限的,它们是 HTML5 规范最强大的特性之一。

表单控件

HTML5 引入了新的表单控件类型,为用户提供了更好的数据输入。这些控件可通过input标签的type属性访问,并为应用用户提供更好的数据输入体验。支持的新输入类型列举如下:

  • color:这给用户呈现了一个拾色器。
  • date:这为用户提供了选择日期的机制。
  • datetime:这允许用户输入日期和时间,包括时区。
  • datetime-local:这允许用户输入他们本地的日期和时间。
  • email:这允许用户输入电子邮件地址。
  • month:这为用户提供了选择月份的机制。
  • number:允许用户输入数字。
  • range:这为用户提供了一个控件,通常是一个滑块,用于选择一个范围内的值。
  • search:这将输入转换为搜索框。
  • tel:这是用来收集电话号码的。
  • time:这为用户输入没有时区的时间提供了控制。
  • url:这为用户提供了一个输入网址的控件。
  • week:这允许用户输入特定的一周。

你可能会问自己这些有什么不同。事实上,这些类型提示浏览器如何收集被请求的信息。当遇到这些输入类型时,大多数浏览器将提供一个自定义控件来代替与输入相关联的传统文本框。

如果不支持input类型,或者如果浏览器不支持 HTML5(或者特别是 HTML5 表单控件),浏览器将呈现类型为text的输入。这是标准规定。未知输入类型将被视为文本。

我们的应用已经利用了一些新的表单控件。如果我们查看我们为添加评论而创建的表单,我们会看到 Opera Mobile 呈现的表单如下:

Form controls

ASP.NET MVC 运行时实际上是基于我们从System.ComponentModel.DataAnnotations添加的验证规则为评级控制生成以下输出。

<input class="text-box single-line"
    data-val="true"
    data-val-number="The field Rating must be a number."
    data-val-required="The Rating field is required."
    id="Rating" name="Rating"
 type="number"
    value="" />

你会注意到在data-*属性中,输入的类型被设置为number。Chrome 和 Opera 看到了这一点,并向用户提供了一个自定义的输入控件来指示要输入的数字。输入不仅是定制的,以帮助用户输入一个数字,它将积极拒绝任何无法转换成数字的输入,并通过代理,执行我们的一些验证规则。

本地存储

本地存储是大多数浏览器厂商支持的字典式存储机制。它与客户端存储数据的传统方式(cookies)的不同之处在于,存储在浏览器本地存储中的信息不会随每个请求一起发送到服务器。对本地存储中的数据的访问是通过键/值对进行的,每个字典只能由创建它的站点访问。

假设我们有一个名为localStorage的变量,它是对浏览器本地存储机制的引用。我们可以通过以下方式之一将信息放入本地存储:

localStorage["key"] = value;
localStorage.setItem("key", value);

同样,从字典中检索数据看起来如下所示:

var value = localStorage["key"]
var value = localStorage.getItem("key");

数据实际上是以字符串的形式存储在字典中的,因此您需要在检索时自己进行类型转换。

地理位置

地理定位 API 为浏览器提供基于位置的服务。如果你需要知道你的用户在哪里,这就是你的应用编程接口。调用此应用编程接口将通知您的用户它的访问,并需要他们的许可。

通过navigator.geolocation物业获得地理定位服务。该属性提供了一个方法getCurrentPosition,该方法将回调方法作为参数。

navigator.geolocation.getCurrentPosition(tell_me);

在前面的例子中,tell_me回调将由地理定位 API 调用,不仅提供纬度和经度,还提供速度和航向等信息。

利用 HTML5 有很多令人兴奋的可能性,幸运的是,大多数移动浏览器都支持它。如果您对目标浏览器支持的功能有疑问,您应该查看mobilehtml5.org提供的功能兼容性表。

CSS3

正如 HTML5 对 HTML 4,CSS3 对 CSS2。它是级联样式表的下一个化身,带来了一些改进,例如名称空间、区域、过滤器和条件样式。

同样,和 HTML5 一样,CSS3 的深度和广度足以填满一系列书籍。事实上,它是如此之大,以至于 CSS3 工作组已经将标准分解成一系列模块,您可以在http://www.w3.org/Style/CSS/current-work查看。

我们将集中讨论媒体类型、选择器和媒体查询,因为它们适用于移动开发,但是在我们开始之前,我想提醒大家,在处理 CSS 时,C 意味着级联。样式将按照元素出现的顺序应用于元素。对你的风格要精确,确保你的元素和风格有有意义的名字。

媒体类型

媒体类型已经存在了一段时间,对 CSS3 来说并不陌生,但由于大多数人对它们并不熟悉,所以值得简单提一下。简而言之,媒体类型可用于为不同的渲染类型指定不同的样式。如果我想在屏幕上查看页面时将一组样式或样式表应用于页面,在打印页面时将一组样式或样式表应用于页面,我可以使用媒体类型来控制这一点。

<link href="styles.css" media="screen" rel="stylesheet">
<link href="print-styles.css" media="print" rel="stylesheet">

撰写本文时支持的媒体类型有allauralbrailleembossedhandheldprintprojectionscreenttytv

在本书的剩余部分,我们将主要使用all媒体类型。

CSS 选择器

CSS 选择器为我们提供了一种基于一条选择标准来选择一个标签或一组标签的机制。选择标准可能像标签的类型一样简单,也可能更复杂,比如从节点的第四个子节点开始,每隔七个子节点。

选择器有几种类型,它们的用法因类型而异,因此我们将简要讨论每种类型,并回顾几个示例,以便为我们提供足够的信息来应对危险。然而,这些信息将允许我们对这个主题做一些进一步的阅读,并加深我们的 CSS-fu。

类型选择器

CSS 类型选择器允许我们将一种样式应用于特定类型的所有元素。类型选择器的几个例子可以在我们的酿酒应用的Site.css文件中找到。

html {
    background-color: #e2e2e2;
    margin: 0;
    padding: 0;
}

该选择器表示对于每个html标签(好的,是的,只有一个),设置background-colormarginpadding属性。类型选择器相当简单。

标识选择器

标识选择器允许我们对具有特定标识的元素应用样式。请注意,它适用于任何一致性文档中的元素,并且元素标识是唯一的。标识选择器用#字符表示。

#body {
    background-color: #efeeef;
    clear: both;
    padding-bottom: 35px;
}

渲染时,任何 ID 为body的元素都将应用前面的样式。

属性选择器

如果你想根据一个属性的值或一个属性的存在来选择一个元素或一组元素,你需要使用属性选择器。属性选择器用括号([])表示,它们的选择标准允许您对值应用条件。

#loginForm input[type="checkbox"],
#loginForm input[type="submit"],
#loginForm input[type="button"],
#loginForm button {
    width: auto;
}

来自我们的Site.css样式表的这段代码摘录设置了包含在任何具有loginForm标识的元素中的所有复选框、提交和按钮输入的宽度。属性选择器不限于type属性,也不限于等价性检查。可以查询任何元素的任何属性。下表列举了可以应用于属性选择器的条件:

|

情况

|

比赛

| | --- | --- | | [attr] | 将任意元素与属性{attr}匹配,无论属性值如何。 | | [attr=value] | 匹配任何具有属性{attr}且值为{value}的元素。 | | [attr~=value] | 如果属性{attr}是由空格分隔的单词组成的列表,假设其中一个单词等于{value},条件将匹配。 | | [attr&#124;=value] | 如果属性{attr}的值等于{value}或以{value-}开始,条件将评估为true并导致匹配。 | | [attr^=value] | 如果属性值{attr}{value}开头,则匹配。 | | [attr$=value] | 如果属性值{attr}{value}结束,则匹配。 | | [attr*=value] | 如果属性{attr}的值包含值{value},则条件匹配。 |

类别选择器

类也可以作为 CSS 选择器标准。类别选择器通常用点(.)表示。

.float-left {
    float: left;
}

任何应用了float-left类的类都将应用float: left样式。

由于class也是一个元素的属性,你可以使用属性选择器来定位和选择某些类的元素。

通用选择器

通用选择器用星号(*)表示。它们是全球性的,在实际应用中很少见到,因为它们的存在是隐含的。上一节中给出的示例类选择器也可以重写为:

*.float-left {
    float: left;
}

星号使得选择器的通用范围显式。

伪类选择器

CSS3 中更强大的选择器类之一是伪类选择器。这些选择器允许我们对选择器应用简单的功能。通过使用冒号(:)来表示,伪类选择器提供了机制来获取包含在 文档对象模型 ( DOM 之外的标记信息。

最常见的一组伪类选择器是应用于锚点标签的:link:visited:active:hover选择器。

a:link, a:visited,
a:active, a:hover {
    color: #333;
}

其他伪类选择器提供了一种机制来检索特定位置、选中、启用或禁用元素的子元素。一个特别有趣的伪类选择器是:nth-child选择器,它允许我们指定选择的出现和偏移。:nth-child(4n-1)将从第三个子元素开始选择元素的每四个子元素。

CSS 选择器允许我们查询页面上的元素,但是 CSS 媒体查询允许我们查询显示页面的媒体的功能。

CSS 媒体查询

CSS 媒体查询为我们提供了一种机制,可以根据确定的特定(或当前)媒体类型的特征信息,有选择地应用样式。媒体查询涉及媒体类型和一个或多个特征查询的使用,在应用与查询相关联的样式之前,这些特征查询必须评估为真。

媒体查询可以使用link标签的传统媒体属性或使用 CSS 文件中的@media关键字来应用。

<link 
    rel="stylesheet" 
 media="all and (orientation:landscape)"
    href="landscape.css">
@media all and (orientation:landscape) { /* styles */ }

媒体特征

当与媒体类型结合时,媒体特征完成了媒体查询的功能。关于媒体功能的一个重要注意事项是,大多数功能都支持min-max-前缀。例如,width媒体功能支持min-max-前缀,这意味着不仅可以查询宽度,还可以查询max-widthmin-width

|

特征

|

最小或最大前缀

|

描述

| | --- | --- | --- | | width | 是 | 用于查询媒体设备的当前、最小或最大宽度。 | | height | 是 | 用于查询媒体设备的当前、最小或最大高度。 | | device-width | 是 | 用于确定设备屏幕的实际宽度。这不同于当前窗口的宽度。 | | device-height | 是 | 用于确定设备屏幕的实际高度。这不同于当前窗口的宽度。 | | orientation | 不 | 用于查询媒体的方向。有效值为纵向或横向。 |

如果我们检查我们的Site.css样式表,我们当前有一个媒体查询。

@media only screen and (max-width: 850px)

当我们在本章后面讨论响应设计时,我们将讨论这一行提供的功能。

视口元标记

当谈论移动网络设计时,最后一条相当重要的信息是viewport meta标签。

大多数移动浏览器都假定,而且理所当然地假定,它们试图呈现的任何页面都是为桌面浏览器创作的,并打算在桌面浏览器中查看。在这种情况下,移动浏览器将以 980 像素宽呈现页面(通常,但有些浏览器可能使用替代分辨率),然后缩放呈现的页面以适合设备的屏幕。对于分辨率较低的设备,即使是我们在 Opera Mobile 模拟器中模拟的宽度为 480 像素的三星 Galaxy S II,这也意味着页面会被渲染,然后缩小到原始大小的 50%以下。这使得阅读变得困难,让用户感到沮丧。

当前在我们的_Layout.cshtml文件中作为<meta name="viewport" content="width=device-width" />viewport meta标签是一个meta标签,用于指示移动设备在向用户显示页面时应该使用的分辨率和比例因子。我们的meta标签,通过将内容宽度设置为我们设备的宽度,告诉浏览器以原生分辨率呈现页面,对于我们的 Opera Mobile 模拟器,该分辨率为 480 像素宽。

viewport meta标签有许多属性和属性值。虽然这里提供的设置是默认设置,但我建议您多阅读一些关于该标签的信息,并确定是否有更好的值或值集供您使用。

正是这种价值让我们能够在应用中进行一些响应性设计。

响应性设计

而现在我们已经圆了。在第 3 章介绍 ASP.NET MVC 4中,我们谈到了响应性设计,以及开发试图在任何浏览器窗口中正确呈现自己的应用,并持续响应浏览器窗口大小或页面本身显示的内容的变化意味着什么。

在前一节中,我们了解到在我们的Site.css文件中有一个@media标签。

@media only screen and (max-width: 850px)

该媒体标签包含一组样式,适用于最大宽度为 850 像素的所有屏幕媒体类型,也就是说,适用于大多数移动设备。

我们还了解到,我们的_Layout.cshtml文件包含viewport meta标签,告诉移动浏览器以其原生分辨率呈现页面。让我们花一点时间把所有的东西放在一起。

当我们在分辨率为 850 像素或更低的网络浏览器中加载任何页面时,我们将应用一组样式来使页面在该分辨率下可用。将视口设置为使用移动设备的原生分辨率,并在 Opera 中加载此页面,会应用媒体定义中包含的一组样式。

A responsive design

如果我们桌面上的浏览器窗口低于 850 像素,同样的规则适用;它得到了与我们的移动模拟器相同的待遇。

A responsive design

然而,如果我们将浏览器的分辨率提高到 850 像素之外的宽度,比如 1024 像素,页面看起来会有明显的不同。

A responsive design

我们的导航和徽标现在不再居中,而是分别向右和向左对齐以更好地利用新提供的水平分辨率。就连登录链接的背景等小东西都变了。我们的网站已经调整到显示媒体的宽度。这就是响应性设计的本质。

响应列表

为了使我们的网站更具响应性,我们将对食谱列表进行一些调整。首先,显示原始重力和最终重力对于那些知道如何根据这些数字计算食谱酒精含量的人来说很棒,但这不应该是我们用户的必备技能。我们需要将RecipeDisplayViewModel类的PercentAlcoholByVolume属性添加到列表中。

不幸的是,我们的列表在移动设备上已经很拥挤了。在显示屏上添加另一个字段将使其实际上无法使用。为了适应这一点,我们将使设计具有响应性。对于小于或等于 600 像素的显示器,我们将只显示酒精的体积。如果显示器宽度大于 600 像素,我们将按体积、原始重力和最终重力显示酒精。

我们将首先在位于我们项目的Content文件夹中的Site.css中添加一个名为hide600的样式。样式定义如下:

@media only screen and (max-width: 600px) {
    .hide600 {
        display: none;
    }
}

该样式规定,每当屏幕宽度低于 600 像素时,为应用该样式的元素设置display``none。这基本上隐藏了元素。当屏幕宽度超出 600 像素时,不定义此样式。这会将display的值重置为默认的值。

我们需要将这种风格应用于原始重力和最终重力柱。请记住在表格标题、表格单元格以及响应信号中枢的 JavaScript 中这样做。我们还将在重力场之前添加PercentAlcoholByVolume属性,所以记得在相同的地方调整列表。以下是调整后的表头代码。完整的代码可以在本书附带的包中找到:

<tr>
    <th>
        @Html.DisplayNameFor(model => model[0].Name)
    </th>
    <th>
        @Html.DisplayNameFor(model => model[0].Style)
    </th>
    <th>
        @Html.DisplayNameFor(model => model[0].PercentAlcoholByVolume)
    </th>
    <th class="hide600">
        @Html.DisplayNameFor(model => model[0].OriginalGravity)
    </th>
    <th class="hide600">
        @Html.DisplayNameFor(model => model[0].FinalGravity)
    </th>
@if(Request.IsAuthenticated) {
    <th>
    </th>
}
</tr>

如果我们在谷歌 Chrome 中加载页面,我们会看到所有的列都存在。窗口的宽度超出了我们隐藏重力柱的 600 像素。

A responsive list

然而,我们的 Opera Mobile 浏览器的宽度为 480 像素。在那里查看页面有不同的列集。

A responsive list

处理响应性设计问题并不难,但总会有这样的情况,你既没有时间也没有愿望去创建一个完全响应性的布局。在这些场景中,您可以选择使用一个公共可用的响应设计框架,如 ZURB 的基础 或 Twitter Bootstrap 。这些工具包为您提供了一套完整的模板和类来帮助您满足相应的设计需求。

总结

在这一章中,我们了解了 HTML5,以及它的起源来自对 HTML 实际使用方式的研究和分析。这项研究的成果是一种新的语义标记,它关注的是内容是什么,而不是如何显示。我们还研究了 CSS3,以及它通过使用选择器和媒体查询,根据客户端的能力来最好地显示内容的能力。将这些技术与浏览器对视口控制的支持结合使用,我们可以通过一种称为响应设计的技术来控制用户体验。

在下一章中,我们将了解 ASP.NET MVC 4 内置的功能,它允许我们针对特定的移动设备,我们将定制我们的应用,为用户提供更好的移动体验。