jQuery2 高级教程(十三)
三十三、重构示例:第四部分
在本书这一部分的前几章,我向您介绍了 jQuery Mobile。在本章中,我将构建一个使用 jQuery Mobile 功能的更完整的例子。就其本质而言,jQuery Mobile 比 jQuery UI 简单得多,可用的设计选择也少得多。移动设备开发面临的独特问题进一步限制了 jQuery Mobile 的开发工作。
从基础开始
在第三十二章中,我展示了一个使用分割列表的例子。这个例子是本章的起点,我将用它来构建一些额外的功能。清单 33-1 显示了本章的初始示例文档。
清单 33-1 。本章的起点
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding-top: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center; margin: 20px}
</style>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true>
<li><a href="#basket" class="buy" id="rose">Roses</a>
<a href="#roses">Roses</a></li>
<li><a href="#basket" class="buy" id="orchid">Orchids</a>
<a href="#orchids">Orchids</a> </li>
<li><a href="#basket" class="buy" id="aster">Asters</a>
<a href="#asters">Asters</a> </li>
</ul>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
Basket will go here
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
</div>
</div>
<div id="roses" data-role="page" data-theme="b">
<div data-role="header">
<h1>Roses</h1>
</div>
<div>
<div class="lcontainer">
<img src="rose.png">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
A rose is a woody perennial within the family Rosaceae.
They form a group of erect shrubs, and climbing or trailing plants.
<div><b>Price: $4.99</b></div>
</div>
</div>
</div>
<div id="orchids" data-role="page" data-theme="b">
<div data-role="header">
<h1>Orchids</h1>
</div>
<div>
<div class="lcontainer">
<img src="orchid.png">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
The orchid family is a diverse and widespread family in the order
Asparagales. It is one of the largest families of flowering plants.
<div><b>Price: $10.99</b></div>
</div>
</div>
</div>
<div id="asters" data-role="page" data-theme="b">
<div data-role="header">
<h1>Asters</h1>
</div>
<div>
<div class="lcontainer">
<img src="aster.png">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
The name Aster comes from the Ancient Greek word meaning "star",
referring to the shape of the flower head.
<div><b>Price: $2.99</b></div>
</div>
</div>
</div>
</body>
</html>
以编程方式插入产品
我要做的第一件事是用一些动态创建的页面替换描述每朵花的静态页面。这一改变使我有了一个更紧凑的文档,并且可以方便地添加更多的花朵供用户选择,而无需复制 HTML 元素。我将使用数据模板生成页面,我在第十二章中描述过。数据模板与核心 jQuery 库一起工作,因此也非常适合 jQuery Mobile 应用。我创建了一个名为data.json 的文件,其中包含了我需要的花的数据。清单 33-2 显示了data.json的内容。
清单 33-2 。data.json 文件的内容
[{ "name": "aster",
"label": "Asters",
"price": "$2.99",
"text": "The name Aster comes from the Ancient Greek word meaning star..."
},{ "name": "carnation",
"label": "Carnations",
"price": "$1.99",
"text": "Carnations require well-drained, neutral to slightly alkaline soil..."
},{ "name": "daffodil",
"label": "Daffodils",
"price": "$1.99",
"text": "Daffodil is a common English name, sometimes used for all varieties..."
},{ "name": "rose",
"label": "Roses",
"price": "$4.99",
"text": "A rose is a woody perennial within the family Rosaceae. They form a..."
},{ "name": "orchid",
"label": "Orchids",
"price": "$10.99",
"text": "The orchid family is a diverse and widespread family in the order..."
}]
数据描述了五种花。对于它们中的每一个,我都定义了产品名称、显示给用户的标签、单价和文本描述。
注意我没有在清单中展示全文描述,但是它包含在
data.json文件中,该文件是本书源代码下载的一部分(可以从Apress.com获得)。
现在我有了数据,我可以将它集成到文档中。清单 33-3 展示了使用数据模板从静态页面到程序生成页面的变化。
清单 33-3 。动态添加页面
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script src="handlebars.js"></script>
<script src="handlebars-jquery.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding-top: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center}
</style>
<script id="flowerTmpl" type="text/x-handlebars-template">
{{#products}}
<div id="{{name}}" data-role="page" data-theme="b">
<div data-role="header">
<h1>{{label}}</h1>
</div>
<div>
<div class="lcontainer">
<img src="{{name}}.png">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
{{text}}
<div><b>Price: {{price}}</b></div>
</div>
</div>
</div>
{{/products}}
</script>
<script id="liTmpl" type="text/x-handlebars-template">
{{#products}}
<li>
<a href="#basket" class="buy" id="A1">{{label}}</a>
<a href="#{{name}}">{{label}}</a>
</li>
{{/products}}
</script>
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("#flowerTmpl").template({ products: data })
.filter("*").appendTo("body");
$("ul").append($("#liTmpl").template({ products: data })
.filter("*")).listview("refresh")
});
initComplete = true;
}
})
</script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true></ul>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
Basket will go here
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
</div>
</div>
</body>
</html>
我移除了每朵花的页面,并使用数据模板从数据中生成我需要的内容,这些数据是我使用getJSON方法获得的(在第十四章中描述)。这一变化的关键是简单的定制 JavaScript 代码,如下所示:
..
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("#flowerTmpl").template({ products: data })
.filter("*").appendTo("body");
$("ul").append($("#liTmpl").template({ products: data })
.filter("*")).listview("refresh")
});
initComplete = true;
}
})
</script>
...
当我获得数据时,我使用模板从数据中生成元素,并将动态生成的页面添加到文档中的body元素。我还使用一个模板来生成主鲜花列表的项目。我告诉 jQuery Mobile 我已经修改了列表的内容,这是通过调用listview小部件上的refresh方法来完成的,如下所示:
...
$("ul").append($("#liTmpl").template({ products: data })
.filter("*")).listview("refresh");
...
数据模板很简单,使用了我在第十二章中描述的标准技术。您可以在图 33-1 中看到结果——一个列表,其项目以编程方式生成,并链接到已经以编程方式添加到文档中的页面。
图 33-1 。以编程方式生成的列表项和页面
重用页面
我喜欢数据模板方法,因为它展示了 jQuery 如何支持如此广泛的功能,允许您将模板等功能与 jQuery Mobile 等接口工具包结合在一起。
也就是说,您可以采用一种更优雅的方法来处理每朵花的页面。您可以生成一组元素并修改它们来显示用户选择的花,而不是为您想要显示的每朵花生成一组元素。清单 33-4 展示了使这成为可能的对文档的修改。
清单 33-4 。为多个产品重复使用一个页面
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script src="handlebars.js"></script>
<script src="handlebars-jquery.js"></script>
<script id="liTmpl" type="text/x-handlebars-template">
{{#products}}
<li>
<a href="#basket" class="buy" id="{{name}}">{{label}}</a>
<a class="productLink" data-flower="{{name}}" href="#">{{label}}</a>
</li>
{{/products}}
</script>
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl").template({ products: data })
.filter("*")).listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
$.mobile.changePage("#productPage");
break;
}
}
})
});
initComplete = true;
}
})
</script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding-top: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center}
</style>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true>
</ul>
</div>
</div>
<div id="productPage" data-role="page" data-theme="b">
<div data-role="header">
<h1 id="header"></h1>
</div>
<div>
<div class="lcontainer">
<img id="image" src="">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
<span id="description"></span>
<div><b>Price: <span id="price"></span></b></div>
</div>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
Basket will go here
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
</div>
</div>
</body>
</html>
提示这是一种特别适合 jQuery Mobile 的方法,因为多个页面包含在一个 HTML 文档中。通常,由于移动设备固有的局限性,您希望尽可能保持 HTML 文档的简单性。
我从文档中删除了一个数据模板,并添加了一个新页面(它的id是productPage),我用它来表示每一朵花。我修改了用于生成列表项的模板,以便在href属性中没有目标页面,并添加我自己的数据属性,以便我知道任何给定链接与哪朵花相关。当从 JSON 中检索到数据后,修改后的脚本从我刚刚使用模板创建的列表元素中选择所有针对每个产品的链接,并绑定到tap事件。当点击一个列表项时,我找到合适的数据项并使用其属性来配置productPage页面,设置向用户显示的文本和图像,如下所示:
...
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl").template({ products: data })
.filter("*")).listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
$.mobile.changePage("#productPage");
break;
}
}
})
});
initComplete = true;
}
})
</script>
...
在我配置页面之后,我使用changePage方法来触发导航。这个例子的外观没有变化,但是移动浏览器需要管理的元素更少了,这是一个很好的例子,说明了如何操作 jQuery Mobile 文档的页面结构。
创建购物车
我在这个例子中使用了一个分割列表,列表项的左侧指向basket页面。在这一节中,我将定义页面的元素并添加一些 JavaScript,这样就有了一个简单的篮子。清单 33-5 显示了对文档的修改。
清单 33-5 。实施购物篮
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script src="handlebars.js"></script>
<script src="handlebars-jquery.js"></script>
<script id="liTmpl" type="text/x-handlebars-template">
{{#products}}
<li>
<a href="#" class="buy" id="{{name}}">{{label}}</a>
<a class="productLink" data-flower="{{name}}" href="#">{{label}}</a>
</li>
{{/products}}
</script>
<script id="trTmpl" type="text/x-handlebars-template">
<tr data-price="{{price}}" id="{{name}}"><td>{{label}}</td><td id="count">1</td>
<td id="subtotal">0</td></tr>
</script>
<script type="text/javascript">
$(document).ready(function () {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl").template({ products: data }))
.filter("*").listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
$.mobile.changePage("#productPage");
break;
}
}
});
$("a.buy").bind("tap", function () {
var targetFlower = this.id;
var row = $("#basketTable tbody #" + targetFlower);
if (row.length > 0) {
var countCell = row.find("#count");
countCell.text(Number(countCell.text()) + 1);
} else {
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
$("#trTmpl").template(data[i])
.appendTo("#basketTable tbody")
break;
}
}
}
calculateTotals();
$.mobile.changePage("#basket")
});
})
})
function calculateTotals() {
var total = 0;
$("#basketTable tbody").children().each(function (index, elem) {
var count = Number($(elem).find("#count").text())
var price = Number($(elem).attr("data-price").slice(1))
var subtotal = count * price;
$(elem).find("#subtotal").text("$" + subtotal.toFixed(2));
total += subtotal;
})
$("#total").text("$" + total.toFixed(2))
}
</script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding-top: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center}
table {display: inline-block; margin: auto; margin-top: 20px; text-align: left;
border-collapse: collapse}
td {min-width: 100px}
th, td {text-align: right}
th:nth-child(1), td:nth-child(1) {text-align: left}
</style>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true></ul>
</div>
</div>
<div id="productPage" data-role="page" data-theme="b">
<div data-role="header">
<h1 id="header"></h1>
</div>
<div>
<div class="lcontainer">
<img id="image" src="">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
<span id="description"></span>
<div><b>Price: <span id="price"></span></b></div>
</div>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
<table id="basketTable" border=0>
<thead>
<tr><th>Flower</th><th>Quantity</th><th>Subtotal</th></tr>
</thead>
<tbody></tbody>
<tfoot>
<tr><th colspan=2>Total:</th><td id="total"></td></tr>
</tfoot>
</table>
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
<button data-inline="true">Checkout</button>
</div>
</div>
</body>
</html>
我向basket页面添加了一个table,它为每个选中的产品显示一行。每行显示产品名称、数量和小计。表格中有一个页脚显示了总的总数。我绑定到了tap事件,这样当用户点击左侧的拆分按钮时,要么向表中添加一个新行,要么如果表中已经有该产品的一行,数量就会增加。新行是使用另一个数据模板生成的,其他一切都是通过读取文档中元素的内容来处理的。
我使用 DOM 本身来确定和维护客户购物篮的整个状态。我本来可以创建一个 JavaScript 对象来对订单进行建模,并从该对象中驱动表的内容,但是在一本关于 jQuery 的书中,我喜欢利用一切机会来处理文档本身。结果是一个简单的篮子,如图图 33-2 所示。
图 33-2 。购物篮页面
添加数量变化
篮子是功能性的,但是如果用户想要两朵玫瑰,例如,她必须点击Rose列表项目,点击Back按钮,然后再次点击Rose项目。这个过程非常荒谬,所以为了更容易地改变产品的数量,我在表中添加了一些input元素。您可以在清单 33-6 中看到这些变化。
清单 33-6 。将范围滑块添加到购物篮表格
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script src="handlebars.js"></script>
<script src="handlebars-jquery.js"></script>
<script id="liTmpl" type="text/x-handlebars-template">
{{#products}}
<li>
<a href="#" class="buy" id="{{name}}">{{label}}</a>
<a class="productLink" data-flower="{{name}}" href="#">{{label}}</a>
</li>
{{/products}}
</script>
<script id="trTmpl" type="text/x-handlebars-template">
<tr data-theme="b" data-price="{{price}}" id="{{name}}"><td>{{label}}</td>
<td id="count"><input type=number value=1 min=0 max=10></td>
<td id="subtotal">0</td>
</tr>
</script>
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl")
.template({ products: data })).listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
$.mobile.changePage("#productPage");
break;
}
}
})
$("a.buy").bind("tap", function () {
var targetFlower = this.id;
var row = $("#basketTable tbody #" + targetFlower);
if (row.length > 0) {
var countCell = row.find("#count input");
countCell.val(Number(countCell.val()) + 1);
} else {
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
$("#trTmpl").template(data[i])
.appendTo("#basketTable tbody")
.find("input").textinput()
break;
}
}
}
calculateTotals();
$.mobile.changePage("#basket")
})
$(document).on("change click", "input", function (event) {
calculateTotals();
})
});
initComplete = true;
}
})
function calculateTotals() {
var total = 0;
$("#basketTable tbody").children().each(function (index, elem) {
var count = Number($(elem).find("#count input").val())
var price = Number($(elem).attr("data-price").slice(1))
var subtotal = count * price;
$(elem).find("#subtotal").text("$" + subtotal.toFixed(2));
total += subtotal;
})
$("#total").text("$" + total.toFixed(2))
}
</script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding-top: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center}
table {display: inline-block; margin: auto; margin-top: 20px; text-align: left;
border-collapse: collapse}
td {min-width: 100px; padding-bottom: 10px}
td:nth-child(2) {min-width: 75px; width: 75px}
th, td {text-align: right}
th:nth-child(1), td:nth-child(1) {text-align: left}
input[type=number] {background-color: white}
tfoot tr {border-top: medium solid black}
tfoot tr td {padding-top: 10px}
</style>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true></ul>
</div>
</div>
<div id="productPage" data-role="page" data-theme="b">
<div data-role="header">
<h1 id="header"></h1>
</div>
<div>
<div class="lcontainer">
<img id="image" src="">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
<span id="description"></span>
<div><b>Price: <span id="price"></span></b></div>
</div>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
<table id="basketTable" border=0>
<thead>
<tr><th>Flower</th><th>Quantity</th><th>Subtotal</th></tr>
</thead>
<tbody></tbody>
<tfoot>
<tr><th colspan=2>Total:</th><td id="total"></td></tr>
</tfoot>
</table>
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
<button data-inline="true">Checkout</button>
</div>
</div>
</body>
</html>
我在模板的 quantity 单元格中插入了一个input元素,用于为表格生成行。这个input元素的type是number,这导致一些浏览器在文本输入区域旁边插入小的上下按钮。这些按钮太小,不适合触摸,但浏览器也会过滤字符,丢弃任何不适合数字的内容。虽然对于本章来说这是可以接受的,但是对于实际项目来说这不是一个完美的方法,因为它支持浮点数,这意味着用户可以输入产品的分数。
在 jQuery Mobile 增强了页面之后,当我向文档添加input元素时,我调用了textinput方法:
...
$("#trTmpl").template(data[i]).appendTo("#basketTable tbody").find("input").textinput()
...
如果我不添加这个方法调用,浏览器会显示本机的input元素。调用textinput方法会导致 jQuery Mobile 增强元素,尽管它没有正确分配样本。所以我为input元素定义了一个样式来设置一致的背景颜色:
...
input[type=number] {background-color: white}
...
我需要更频繁地计算小计和总计,因为用户可以在购物篮页面中更改产品的数量。因为我在应用的整个生命周期中向文档添加了input元素,所以我使用 jQuery on方法来处理事件。on方法在第九章中有描述。下面是事件处理程序代码:
...
$(document).on("change click", "input", function (event) {
calculateTotals();
})
...
我使用on方法将我的处理函数与change和click事件关联起来。为数字input元素添加向上和向下按钮的浏览器在这些按钮被按下时会触发 click 事件,所以除了更令人期待的change事件之外,我还需要处理这个事件。当任一事件被触发时,我的处理函数调用calculateTotals函数。你可以在图 33-3 中看到篮子的样子。
图 33-3 。向购物篮页面添加输入元素
向信息页面添加按钮
产品信息描述了用户选择的花,但是它没有为用户提供任何将它添加到购物篮的方法。为了完善基本的购物篮功能,我在产品页面上添加了一个按钮,将商品添加到购物篮中。清单 33-7 显示了产品页面的变化。
清单 33-7 。向产品页面添加按钮
...
<div id="productPage" data-role="page" data-theme="b">
<div data-role="header">
<h1 id="header"></h1>
</div>
<div>
<div class="lcontainer">
<img id="image" src="">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
<span id="description"></span>
<div>
<b>Price: <span id="price"></span></b>
<a href="#" id="buybutton" data-flower="" data-role="button"
data-inline=true>Buy</a>
</div>
</div>
</div>
</div>
...
我定义了一个a元素,jQuery Mobile 将把它转换成一个按钮小部件。我添加了一个数据属性(data-flower),这样当用户点击按钮时,我可以跟踪显示的是哪朵花。为了支持这个按钮,我对脚本做了一些修改。这些变化显示在清单 33-8 中。
清单 33-8 。在脚本中添加对购买按钮的支持
...
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl")
.template({ products: data })).listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
page.find("#buybutton").attr("data-flower", data[i].name);
$.mobile.changePage("#productPage");
break;
}
}
})
$("#buybutton").bind("tap", function () {
addProduct($(this).attr("data-flower"));
})
$("a.buy").bind("tap", function () {
addProduct(this.id);
})
function addProduct(targetFlower) {
var row = $("#basketTable tbody #" + targetFlower);
if (row.length > 0) {
var countCell = row.find("#count input");
countCell.val(Number(countCell.val()) + 1);
} else {
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
$("#trTmpl").template(data[i])
.appendTo("#basketTable tbody")
.find("input").textinput()
break;
}
}
}
calculateTotals();
$.mobile.changePage("#basket")
}
$(document).on("change click", "input", function (event) {
calculateTotals();
})
});
initComplete = true;
}
})
function calculateTotals() {
var total = 0;
$("#basketTable tbody").children().each(function (index, elem) {
var count = Number($(elem).find("#count input").val())
var price = Number($(elem).attr("data-price").slice(1))
var subtotal = count * price;
$(elem).find("#subtotal").text("$" + subtotal.toFixed(2));
total += subtotal;
})
$("#total").text("$" + total.toFixed(2))
}
</script>
...
这些变化非常简单。当用户从主列表中选择一个产品时,我在a元素上设置了data-flower属性的值。我注册了一个函数来处理按钮的tap事件,并使用值data-flower来调用addProduct函数,该函数包含我从另一个处理函数中提取的代码。通过这些更改,用户可以从主列表(通过点击拆分列表项目的左侧)或从信息页面(通过点击Buy按钮)向购物篮添加产品。图 33-4 显示了页面上添加的Buy按钮。
图 33-4 。向产品信息页面添加按钮
实现结账流程
为了完善这个例子,我将演示如何从各种 jQuery Mobile 页面收集数据,收集的数据可以用于发出 Ajax 请求。我不会发出请求本身,也不会实现服务器。jQuery Mobile 使用了对 Ajax 的核心 jQuery 支持,我在第十四章和第十五章描述了这一点。清单 33-9 显示了当点击Checkout按钮时显示给用户的页面,以及收集数据的处理函数。
清单 33-9 。实施结账流程
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="jquery.mobile-1.3.1.css" type="text/css" />
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script src="handlebars.js"></script>
<script src="handlebars-jquery.js"></script>
<script id="liTmpl" type="text/x-handlebars-template">
{{#products}}
<li>
<a href="#" class="buy" id="{{name}}">{{label}}</a>
<a class="productLink" data-flower="{{name}}" href="#">{{label}}</a>
</li>
{{/products}}
</script>
<script id="trTmpl" type="text/x-handlebars-template">
<tr data-theme="b" data-price="{{price}}" id="{{name}}"><td>{{label}}</td>
<td id="count"><input type=number value=1 min=0 max=10></td>
<td id="subtotal">0</td>
</tr>
</script>
<script type="text/javascript">
var initComplete = false;
$(document).bind("pageinit", function () {
if (!initComplete) {
$.getJSON("data.json", function (data) {
$("ul").append($("#liTmpl")
.template({ products: data })).listview("refresh");
$("a.productLink").bind("tap", function () {
var targetFlower = $(this).attr("data-flower");
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
var page = $("#productPage");
page.find("#header").text(data[i].label);
page.find("#image").attr("src", data[i].name + ".png");
page.find("#description").text(data[i].text);
page.find("#price").text(data[i].price);
page.find("#buybutton")
.attr("data-flower", data[i].name);
$.mobile.changePage("#productPage");
break;
}
}
})
$("#buybutton").bind("tap", function () {
addProduct($(this).attr("data-flower"));
})
$("a.buy").bind("tap", function () {
addProduct(this.id);
})
function addProduct(targetFlower) {
var row = $("#basketTable tbody #" + targetFlower);
if (row.length > 0) {
var countCell = row.find("#count input");
countCell.val(Number(countCell.val()) + 1);
} else {
for (var i = 0; i < data.length; i++) {
if (data[i].name == targetFlower) {
$("#trTmpl").template(data[i])
.appendTo("#basketTable tbody")
.find("input").textinput();
break;
}
}
}
calculateTotals();
$.mobile.changePage("#basket")
}
$(document).on("change click", "input", function (event) {
calculateTotals();
})
$("#submit").bind("tap", function () {
var dataObject = new Object();
$("#basketTable tbody").children().each(function (index, elem) {
dataObject[elem.id] = $(elem).find("#count input").val();
})
dataObject["name"] = $("#name").val();
dataObject["wrap"] = $("option:selected").val();
dataObject["shipping"] = $("input:checked").attr("id")
console.log("DATA: " + JSON.stringify(dataObject))
})
});
initComplete = true;
}
})
function calculateTotals() {
var total = 0;
$("#basketTable tbody").children().each(function (index, elem) {
var count = Number($(elem).find("#count input").val())
var price = Number($(elem).attr("data-price").slice(1))
var subtotal = count * price;
$(elem).find("#subtotal").text("$" + subtotal.toFixed(2));
total += subtotal;
})
$("#total").text("$" + total.toFixed(2))
}
</script>
<script type="text/javascript" src="jquery.mobile-1.3.1.js"></script>
<style type="text/css">
.lcontainer {float: left; text-align: center; padding: 10px}
.productData {float: right; padding: 10px; width: 60%}
.cWrapper {text-align: center}
table {display: inline-block; margin: auto; margin-top: 20px; text-align: left;
border-collapse: collapse}
td {min-width: 100px; padding-bottom: 10px}
td:nth-child(2) {min-width: 75px; width: 75px}
th, td {text-align: right}
th:nth-child(1), td:nth-child(1) {text-align: left}
input[type=number] {background-color: white}
tfoot tr {border-top: medium solid black}
tfoot tr td {padding-top: 10px}
</style>
</head>
<body>
<div id="page1" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div id="container" style="padding: 20px">
<ul data-role="listview" data-inset=true></ul>
</div>
</div>
<div id="productPage" data-role="page" data-theme="b">
<div data-role="header">
<h1 id="header"></h1>
</div>
<div>
<div class="lcontainer">
<img id="image" src="">
<div><a href="#" data-rel="back" data-role="button"
data-inline=true data-direction="reverse">Back</a>
</div>
</div>
<div class="productData">
<span id="description"></span>
<div>
<b>Price: <span id="price"></span></b>
<a href="#" id="buybutton" data-flower="" data-role="button"
data-inline=true>Buy</a>
</div>
</div>
</div>
</div>
<div id="basket" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div class="cWrapper">
<table id="basketTable" border=0>
<thead>
<tr><th>Flower</th><th>Quantity</th><th>Subtotal</th></tr>
</thead>
<tbody></tbody>
<tfoot>
<tr><th colspan=2>Total:</th><td id="total"></td></tr>
</tfoot>
</table>
</div>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline=true
data-direction="reverse">Back</a>
<a href="#checkout" data-role="button" data-inline="true">Checkout</a>
</div>
</div>
<div id="checkout" data-role="page" data-theme="b">
<div data-role="header">
<h1>Jacqui's Shop</h1>
</div>
<div data-role="content">
<label for="name">Name: </label>
<input id="name" placeholder="Your Name">
<label for="wrap"><span>Gift Wrap: </span></label>
<select id="wrap" name="wrap" data-role="slider">
<option value="yes" selected>Yes</option>
<option value="no">No</option>
</select>
<fieldset data-role="controlgroup" data-type="horizontal">
<legend>Shipping:</legend>
<input type="radio" name="ship" id="overnight" checked />
<label for="overnight">Overnight</label>
<input type="radio" name="ship" id="23day"/>
<label for="23day">2-3 days</label>
<input type="radio" name="ship" id="710day"/>
<label for="710day">7-10 days</label>
</fieldset>
<div class="cWrapper">
<a href="#" data-rel="back" data-role="button" data-inline="true"
data-direction="reverse">Back</a>
<a href="#" id="submit" data-role="button"
data-inline="true">Submit Order</a>
</div>
</div>
</div>
</body>
</html>
这个新页面叫做checkout。我保持这个表单简单,提示用户输入姓名,并提供礼物包装和运送方式的选择。你可以在图 33-5 中看到该页面是如何出现的。我以纵向显示页面,因为它允许我显示所有的元素,而不必滚动。
图 33-5 。结账页面
当用户点击Submit Order按钮时,我从 HTML 文档的不同页面收集数据,并将结果作为 JSON 字符串写入控制台。以下是这种字符串的一个示例:
{"carnation":"3","rose":"1","orchid":"1",
"name":"Adam Freeman","wrap":"yes","shipping":"23day"}
摘要
在这一章中,我采用了 jQuery Mobile 的一些核心特性,并将它们组合起来,创建了一个花店示例的简单移动实现。就其本质而言,jQuery Mobile 比 jQuery UI 简单得多。主要的挑战是设计一种方法,在一个小屏幕的范围内给用户提供他所需要的信息。
三十四、使用 jQuery 工具方法
jQuery 包括许多实用方法,它们对 jQuery 对象执行高级操作,或者补充 JavaScript 语言以提供编程语言中通常存在的特性。您可能永远都不需要这些方法,但是 jQuery 在内部使用它们,公开使用它们意味着当您遇到 jQuery 团队已经解决的奇怪问题时,您可以节省时间和精力。
其中一些方法应用于 jQuery 对象,一些方法针对主 jQuery 函数调用,我已经用$符号进行了说明(在第五章中进行了描述)。表 34-1 对本章进行了总结。
表 34-1 。章节总结
| 问题 | 解决办法 | 列表 |
|---|---|---|
| 将操作排队以供以后执行。 | 使用通用队列。 | 1, 2 |
| 过滤数组的内容。 | 使用grep方法。 | 3, 4 |
| 确定数组是否包含特定的对象或值。 | 使用inArray方法。 | five |
| 投影数组的内容。 | 使用map方法。 | 6, 7 |
| 连接两个数组。 | 使用merge方法。 | eight |
从一个jQuery对象中删除重复项,并按照它们在文档中出现的顺序对它们进行排序。 | 使用unique方法。 | nine |
| 确定对象的类型。 | 使用isXXX或type方法。 | 10, 11 |
| 准备提交表单的内容。 | 使用serialize或serializeArray方法。 | Twelve |
| 将数据解析成更有用的形式。 | 使用parseJSON或parseXML方法。 | Thirteen |
| 从字符串中删除前导和尾随空格。 | 使用trim方法。 | Fourteen |
| 确定一个元素是否包含另一个元素。 | 使用contains方法。 | Fifteen |
重新访问队列:使用通用队列
在第十章中,我向您展示了如何使用 jQuery 效果队列来管理应用于一组元素的一连串效果。事实上,效果队列只是一个队列,这个特性是一个通用的队列,可以有更广泛的用途。表 34-2 重述了队列相关的方法,为通用目的进行了调整。
表 34-2 。队列方法
| 方法 | 描述 |
|---|---|
clearQueue(<name>) | 移除指定队列中尚未运行的任何函数。 |
queue(<name>) | 返回要对jQuery对象中的元素执行的函数的指定队列。 |
queue(<name>, function) | 将函数添加到队列的末尾。 |
dequeue(<name>) | 为jQuery对象中的元素删除并执行队列中的第一项。 |
delay(<time>, <name>) | 在指定队列中的效果之间插入延迟。 |
当在没有指定队列名的情况下使用这些方法时,jQuery 默认为fx,这是用于视觉效果的队列。我可以使用任何其他队列名来创建函数队列。
当将 jQuery 队列应用于一般用途时,我使用clearQueue方法而不是stop方法–stop对不适于更广泛使用的 jQuery 效果有特殊支持。清单 34-1 提供了一个使用通用队列的例子。
清单 34-1 。使用队列
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<script type="text/javascript">
$(document).ready(function() {
var elems = $("input");
elems.queue("gen", function(next) {
$(this).val(100).css("border", "thin red solid");
next();
});
elems.delay(1000, "gen");
elems.queue("gen", function(next) {
$(this).val(0).css("border", "");
$(this).dequeue("gen");
});
$("<button>Process Queue</button>").appendTo("#buttonDiv")
.click(function(e) {
elems.dequeue("gen");
e.preventDefault();
});
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<form method="post">
<div id="oblock">
<div class="dtable">
<div id="row1" class="drow">
<div class="dcell">
<img src="aster.png"/><label for="aster">Aster:</label>
<input name="aster" value="0" required />
</div>
<div class="dcell">
<img src="daffodil.png"/><label for="daffodil">Daffodil:</label>
<input name="daffodil" value="0" required />
</div>
<div class="dcell">
<img src="rose.png"/><label for="rose">Rose:</label>
<input name="rose" value="0" required />
</div>
</div>
<div id="row2"class="drow">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" required />
</div>
<div class="dcell">
<img src="primula.png"/><label for="primula">Primula:</label>
<input name="primula" value="0" required />
</div>
<div class="dcell">
<img src="snowdrop.png"/><label for="snowdrop">Snowdrop:</label>
<input name="snowdrop" value="0" required />
</div>
</div>
</div>
</div>
<div id="buttonDiv"><button type="submit">Place Order</button></div>
</form>
</body>
</html>
提示我在这个例子中引用的
styles.css文件是我在本书第二部分中使用的文件。
在这个例子中,我将三个函数添加到一个名为gen 的队列中,该队列对文档中的input元素进行操作。首先,我使用val方法将所有的input值设置为100,使用css方法添加一个边框。第二,我使用 delay 方法向队列添加一秒钟的延迟。最后,我使用val和css方法将input元素重置为它们的初始状态。
我还在文档中添加了一个button,它调用了dequeue方法——与效果队列不同,我自己负责启动队列处理。你可以在图 34-1 中看到效果。
图 34-1 。使用通用队列
我放在队列中的函数的工作方式与事件队列中的相同,和以前一样,我负责调用dequeue方法或调用作为参数传递的函数。我倾向于使用函数参数——只是因为我在调用dequeue方法时经常忘记指定队列名,这意味着我的队列陷入停顿。
手动处理队列项目
当然,您不必从另一个函数中触发一个队列函数——您可以依靠外部触发器来dequeue每个项目,比如用户按下我添加到文档中的按钮。清单 34-2 展示了如何做到这一点。
清单 34-2 。显式出队函数
...
<script type="text/javascript">
$(document).ready(function() {
$("input").queue("gen", function() {
$(this).val(100).css("border", "thin red solid");
}).queue("gen", function() {
$(this).val(0).css("border", "");
}).queue("gen", function() {
$(this).css("border", "thin blue solid");
$("#dequeue").attr("disabled", "disabled");
});
$("<button id=dequeue>Dequeue Item</button>").appendTo("#buttonDiv")
.click(function(e) {
$("input").dequeue("gen");
e.preventDefault();
});
});
</script>
...
在这个脚本中,我将queue调用链接在一起,并添加了一个函数,该函数设置所选元素的边框并禁用button元素,必须单击该元素才能处理队列中的每个项目——没有自动链接。
数组的实用方法
jQuery 提供了许多使用数组的有用方法——这些方法在表 34-3 中有描述。在大多数情况下,使用HTMLElement数组有更好的方法——只需使用标准的 jQuery 方法来处理和过滤元素。对于其他类型的数组,这些方法可能会有所帮助。
表 34-3 。使用数组的实用方法
| 方法 | 描述 |
|---|---|
$.grep(<array>, function) $.grep(<array>, function, <invert>) | 基于函数过滤数组的内容。 |
$.inArray(<value>, <array>) | 确定数组中是否包含特定的项。 |
$.map(<array>, function) $.map(<array>, <map>) | 使用函数投影数组或映射对象。 |
$.merge(<array>, <array>) | 将第二个数组的内容追加到第一个数组中。 |
$.unique(HTMLElement[]) | 将一组HTMLElement对象按文档顺序排序,并删除任何重复的对象。 |
使用 Grep 方法
grep方法允许我们找到一个数组中所有与过滤函数匹配的元素。清单 34-3 展示了这种方法。
清单 34-3 。使用 Grep 方法
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
var filteredArray = $.grep(flowerArray, function(elem, index) {
return elem.indexOf("p") > -1;
});
for (var i = 0; i < filteredArray.length; i++) {
console.log("Filtered element: " + filteredArray[i]);
}
});
</script>
...
我们的过滤函数传递了两个参数——第一个是数组中的元素,第二个是该元素的数组索引。对于数组中的每一项都调用我们的函数,如果当前项包含在筛选结果中,则返回true。
在这个例子中,我对字符串数组使用了grep方法,过滤掉了那些不包含字母p的字符串。我将过滤后的数组的内容写入控制台,产生以下结果:
Filtered element: peony
Filtered element: primula
Filtered element: snowdrop
您可以向grep方法提供一个额外的参数——如果这个参数是true,那么过滤过程将被反转,结果将包含该函数过滤掉的那些元素。清单 34-4 显示了这一论点的效果。
清单 34-4 。使用 Grep 方法反转选择
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
var filteredArray = $.grep(flowerArray, function(elem, index) {
return elem.indexOf("p") > -1;
},true);
for (var i = 0; i < filteredArray.length; i++) {
console.log("Filtered element: " + filteredArray[i]);
}
});
</script>
...
这种变化会产生以下结果:
Filtered element: aster
Filtered element: daffodil
Filtered element: rose
使用阵列法
inArray方法确定一个数组是否包含一个指定的值——如果该项在数组中,该方法返回该项的索引,否则返回-1。清单 34-5 展示了inArray方法。
清单 34-5 。使用 inArray 方法
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
console.log("Array contains rose: " +$.inArray("rose", flowerArray));
console.log("Array contains lily: " +$.inArray("lily", flowerArray));
});
</script>
...
这个脚本检查花的数组是否包含rose和lily。结果如下:
Array contains rose: 2
Array contains lily: -1
使用贴图法
map方法使用一个函数将数组或地图对象的内容投影到一个新的数组中,使用一个函数来确定每个项目在结果中的表示方式。清单 34-6 展示了map方法在一个数组中的使用。
清单 34-6 。使用映射方法投影数组
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
var result = $.map(flowerArray, function(elem, index) {
return index + ": " + elem;
});
for (var i = 0; i < result.length; i++) {
console.log(result[i]);
}
});
</script>
...
我们的映射函数针对数组中的每一项执行,并将该项及其在数组中的索引作为参数传递给它。函数的结果包含在由map方法返回的数组中。在这个脚本中,我通过连接值和索引来转换数组中的每一项,产生以下结果:
0: aster
1: daffodil
2: rose
3: peony
4: primula
5: snowdrop
您可以使用 map 方法有选择地投影一个数组——如果您没有从函数中为正在处理的项返回值,结果中将没有相应的项。清单 34-7 显示了如何有选择地从一个数组中投影。
清单 34-7 。选择性映射数组
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
var result = $.map(flowerArray, function(elem, index) {
if (elem != "rose") {
return index + ": " + elem;
}
});
for (var i = 0; i < result.length; i++) {
console.log(result[i]);
}
});
</script>
...
为除rose之外的所有数组值生成结果,这产生以下结果:
0: aster
1: daffodil
3: peony
4: primula
5: snowdrop
使用合并方法
merge方法连接两个数组,如清单 34-8 所示。
清单 34-8 。使用合并方法
...
<script type="text/javascript">
$(document).ready(function() {
var flowerArray = ["aster", "daffodil", "rose", "peony", "primula", "snowdrop"];
var additionalFlowers = ["carnation", "lily", "orchid"];
$.merge(flowerArray, additionalFlowers);
for (var i = 0; i < flowerArray.length; i++) {
console.log(flowerArray[i]);
}
});
</script>
...
第二个数组中的项被追加到第一个数组中,由第一个参数指定的数组被合并过程修改。示例中的脚本产生以下结果:
aster
daffodil
rose
peony
primula
snowdrop
carnation
lily
orchid
使用独特的方法
unique方法将一组HTMLElement对象按照它们在文档中出现的顺序进行排序,并删除任何重复的元素。清单 34-9 展示了如何使用这个方法。
清单 34-9 。使用独特的方法
...
<script type="text/javascript">
$(document).ready(function() {
var selection = $("img[src*=rose], img[src*=primula]").get();
$.merge(selection, $("img[src*=aster]"));
$.merge(selection, $("img"));
$.unique(selection);
for (var i =0; i < selection.length; i++) {
console.log("Elem: " + selection[i].src);
}
});
</script>
...
排序过程就地完成,这意味着作为参数传递给unique方法的数组被修改。在这个例子中,我创建了一个包含重复且不按文档顺序排列的HTMLElement对象的数组,然后应用了unique方法。
类型的实用方法
jQuery 提供了一组用于确定 JavaScript 对象性质的方法——这些方法在表 34-4 中有描述。
表 34-4 。使用类型的实用方法
| 方法 | 描述 |
|---|---|
$.isArray(Object) | 如果对象是数组,则返回true。 |
$.isEmptyObject(Object) | 如果对象没有定义任何方法或属性,则返回true。 |
$.isFunction(Object) | 如果对象是函数,则返回true。 |
$.isNumeric(Object) | 如果对象是数字,则返回true。 |
$.isWindow(Object) | 如果对象是一个Window,则返回true。 |
$.isXMLDoc(Object) | 如果对象是 XML 文档,则返回true。 |
$.type(Object) | 返回对象的内置 JavaScript 类型。 |
这些方法大多数都很简单——将一个对象传递给该方法,如果该对象是该方法检测到的类型,则该方法返回true,否则返回false。作为一个简单的演示,清单 34-10 包含了一个使用isFunction方法 的例子。
清单 34-10 。使用 isFunction 方法
...
<script type="text/javascript">
$(document).ready(function() {
function myFunc() {
console.log("Hello!");
}
console.log("IsFunction: " +$.isFunction(myFunc));
console.log("IsFunction: " +$.isFunction("hello"));
});
</script>
...
在这个例子中,我使用了isFunction方法来测试两个对象。结果如下:
IsFunction: true
IsFunction: false
使用类型方法
type方法略有不同,因为它返回对象的基本 JavaScript 类型。结果将是以下字符串之一:
booleannumberstringfunctionarraydateregexpobject
清单 34-11 展示了type方法的使用。
清单 34-11 。使用类型方法
...
<script type="text/javascript">
$(document).ready(function() {
function myFunc() {
console.log("Hello!");
}
var jq = $("img");
var elem = document.getElementById("row1");
console.log("Type: " +$.type(myFunc));
console.log("Type: " +$.type(jq));
console.log("Type: " +$.type(elem));
});
</script>
...
在这个脚本中,我对一个函数、jQuery对象和HTMLElement对象使用了 type 方法。结果如下:
Type: function
Type: object
Type: object
数据的实用方法
jQuery 定义了许多对处理各种数据有用的实用方法——这些方法在表 34-5 中有描述。
表 34-5 。处理数据的实用方法
| 方法 | 描述 |
|---|---|
serialize() | 将一组表单元素编码成适合提交给服务器的字符串。 |
serializeArray() | 将一组表单元素编码到一个数组中,准备编码到 JSON 中。 |
$.parseJSON(<json>) | 从 JSON 数据创建一个 JavaScript 对象。 |
$.parseXML(<xml>) | 从 XML 字符串创建一个XMLDocument对象。 |
$.trim(String) | 移除字符串开头和结尾的所有空白。 |
序列化表单数据
serialize和serializeArray方法是一种从一组表单元素中提取细节的便捷方式,对于常规或 Ajax 表单提交非常有用。清单 34-12 展示了这两种方法的使用。
清单 34-12 。序列化表单数据
...
<script type="text/javascript">
$(document).ready(function() {
$("<button>Serialize</button>").appendTo("#buttonDiv").click(function(e) {
var formArray = $("form").serializeArray();
console.log("JSON: " + JSON.stringify(formArray))
var formString = $("form").serialize();
console.log("String: " + formString)
e.preventDefault();
});
});
</script>
...
在本例中,我使用这两种方法序列化文档中的表单元素,并将结果写入控制台。serializeArray方法返回一个 JavaScript 数组,其中包含文档中每个表单元素的一个对象。这些对象有两个属性:name属性包含元素的name属性的值,而value属性包含元素的值。以下是示例文档的输出:
[{"name":"aster","value":"1"},{"name":"daffodil","value":"0"}, {"name":"rose","value":"0"},{"name":"peony","value":"0"}, {"name":"primula","value":"2"},{"name":"snowdrop","value":"0"}]
相比之下,serialize 方法创建一个编码字符串,如下所示:
aster=1&daffodil=0&rose=0&peony=0&primula=2&snowdrop=0
解析数据
在处理 Ajax 请求的结果时,parseJSON 和parseXML方法特别有用。对于大多数 web 应用来说,JSON 已经成为首选的数据格式,原因我在第十四章中概述。XML 仍然在使用,但是我发现自己只在将新的应用与遗留的后端系统集成时使用这种 XML 数据。清单 34-13 显示了正在使用的parseJSON方法。
清单 34-13 。解析 JSON 数据
...
<script type="text/javascript">
$(document).ready(function() {
$("<button>Serialize</button>").appendTo("#buttonDiv").click(function(e) {
var jsonData = '{"name": "Adam Freeman", "city": "London", "country": "UK"}'
var dataObject = $.parseJSON(jsonData)
for (var prop in dataObject) {
console.log("Property: " + prop + " Value: " + dataObject[prop])
}
e.preventDefault();
});
});
</script>
...
在这个例子中,我定义了一个简单的 JSON 字符串,并使用parseJSON方法将其转换成一个 JavaScript 对象。然后,我向控制台枚举对象中的属性及其值,生成以下输出:
Property: name Value: Adam Freeman
Property: city Value: London
Property: country Value: UK
修剪琴弦
trim方法 删除字符串开头和结尾的所有空白——包括空格、制表符和换行符。这是大多数编程语言支持的特性,作为其字符数据核心处理的一部分,但是由于某种原因,JavaScript 缺少这一特性。清单 34-14 显示了正在使用的trim方法。
清单 34-14 。使用修剪方法
...
<script type="text/javascript">
$(document).ready(function() {
$("<button>Serialize</button>").appendTo("#buttonDiv").click(function(e) {
var sourceString = "\n This string contains whitespace ";
console.log(">" + sourceString + "<")
var resultString = $.trim(sourceString);
console.log(">" + resultString + "<")
e.preventDefault();
});
});
</script>
...
在这个例子中,我使用了trim方法,将原始的和修整过的字符串写入控制台,产生了以下结果:
> This string contains whitespace <
>This string contains whitespace<
其他实用方法
有许多 jQuery 方法并不完全属于另一个类别,但仍然是有用的——这些方法在表 34-6 中有描述。
表 34-6 。其他实用方法
| 方法 | 描述 |
|---|---|
$.contains(HTMLElement, HTMLElement) | 如果第一个元素包含第二个元素,则返回true。 |
$.now() | 返回当前时间,简写为new Date().getTime()。 |
检查元素包含
方法检查一个元素是否包含另一个元素。两个参数都表示为HTMLElement对象,如果第一个参数表示的元素包含第二个参数表示的元素,则该方法返回 true。清单 34-15 展示了contains方法。
清单 34-15 。使用 Contains 方法
...
<script type="text/javascript">
$(document).ready(function() {
$("img").hover(function(e) {
var elem = document.getElementById("row1");
if ($.contains(elem, this)) {
$(e.target).css("border", e.type == "mouseenter" ?
"thick solid red" : "");
}
});
});
</script>
...
在这个脚本中,我使用 DOM API 获得一个HTMLElement对象,并检查它是否包含传递给事件处理程序方法的元素——如果包含,我为触发事件的元素设置一个边框。
提示这个方法只对
HTMLElement对象有效——如果你想对jQuery对象执行同样的检查,那么考虑使用find方法,我在第六章中描述过。
摘要
在这一章中,我描述了 jQuery 工具方法——一组有用的函数,可用于对 jQuery 对象执行高级操作,或者补充 JavaScript 语言特性以提供程序员通常需要的支持。当您需要这些方法时,您很高兴它们存在,但是对于大多数 web 应用项目来说,您可以放心地忘记它们。
三十五、jQuery UI 效果和 CSS 框架
在这一章中,我描述了 jQuery UI 提供的两个实用特性。第一个是对现有 jQuery 方法的一组增强,可以动态显示颜色、元素可见性的变化以及 CSS 类的应用。另一个特性是一组 CSS 类,它们将 jQuery UI 主题应用到我们的 HTML 文档的其余部分,以便在整个 web 应用中创建一致的外观。表 35-1 对本章进行了总结。
表 35-1 。章节总结
| 问题 | 解决办法 | 列表 |
|---|---|---|
| 动画颜色变化。 | 使用增强的animate方法。 | one |
| 动画类的应用。 | 使用增强的addClass、removeClass和toggleClass方法以及switchClass方法。 | 2, 3 |
| 动画显示可见性转换。 | 使用增强的show、hide和toggle方法。 | four |
| 在不改变元素可见性的情况下应用效果。 | 使用effect方法。 | five |
| 将元素样式化为小部件。 | 使用小部件容器类。 | six |
| 对元素应用圆角。 | 使用角类。 | seven |
| 将可点击小部件的样式应用于元素。 | 使用交互状态类。 | eight |
| 向用户提供关于元素状态的提示。 | 使用提示类。 | 9, 10 |
使用 jQuery UI 效果
jQuery UI 扩展了一些核心的 jQuery 方法来为一个元素制作不同过渡的动画——从颜色变化的动画到 CSS 类的应用。如果小心使用的话,这些对于 web 应用来说是很有价值的补充,为了补充这些特性,jQuery UI 还定义了一些额外的动画效果。
动画颜色
jQuery UI 扩展了 jQuery animate方法,我在第十章中描述过,增加了对动画颜色的支持。您可以将定义元素颜色的多个 CSS 属性之一制作成动画。表 35-2 描述了animate方法支持的 CSS 属性。
表 35-2 。jQuery UI Animate 方法支持的 CSS 属性
| 财产 | 描述 |
|---|---|
backgroundColor | 设置元素的背景色。 |
borderTopColor``borderBottomColor``borderLeftColor | 设置元素边框各边的颜色。 |
color | 设置元素的文本颜色。 |
outlineColor | 设置轮廓的颜色,用于强调元素。 |
若要制作颜色动画,请将 map 对象作为参数传递给 animate 方法,详细说明要制作动画的属性和目标值。清单 35-1 包含了一个例子。
清单 35-1 。动画颜色
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
#animTarget {
background-color: white;
color: black;
border: medium solid black;
width: 200px; height: 50px;
text-align: center;
font-size: 25px;
line-height: 50px;
display: block;
margin-bottom: 10px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("button").click(function() {
$("#animTarget").animate({
backgroundColor: "black",
color: "white"
})
})
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div id=animTarget>
Hello!
</div>
<button>Animate Color</button>
</body>
</html>
我设计了这个文档中的div元素的样式,使它的初始背景颜色为white,颜色为black。当单击文档中的按钮时,我调用 animate 方法,指定将这些属性分别更改为black和white。从一种颜色到另一种颜色的过渡是逐渐完成的,两种属性同时激活。在图 35-1 中可以看到效果。
图 35-1 。动画颜色
提示注意,我在
style元素中使用了标准的 CSS 属性名,例如–background-color。但是当在 map 对象中指定相同的属性时,我改用了 camel case—backgroundColor。这允许我将 CSS 属性指定为 JavaScript 对象属性,而不必用引号将该术语括起来。
在这个例子中,我使用 CSS 颜色简写值black和white指定了我想要的颜色。有许多颜色的简写值,但是animate方法也将接受十六进制颜色(例如#FFFFFF)和 RGB 函数颜色,例如rgb(255, 255, 255)。
提示除了对颜色属性的支持,你还可以使用
animate方法,就像我在第十章中描述的那样。
动画类
jQuery UI 提供了一种使用类来制作 CSS 属性集动画的便捷方式。您只需在一个类中定义属性和值,并告诉 jQuery UI 将该类添加到一个或多个元素中,而不是指定每个属性。jQuery UI 将动画显示从一种状态到另一种状态的转换。清单 35-2 提供了一个演示。
清单 35-2 。使用类制作动画
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
.elemClass {
background-color: white;
color: black;
border: medium solid black;
width: 200px; height: 50px;
text-align: center;
font-size: 25px;
line-height: 50px;
display: block;
margin-bottom: 10px;
}
.myClass {
font-size: 40px; background-color: black; color: white;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("button").click(function() {
if (this.id == "add") {
$("#animTarget").addClass("myClass", "fast")
} else {
$("#animTarget").removeClass("myClass", "fast")
}
})
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div id=animTargetclass="elemClass">
Hello!
</div>
<button id="add">Add Class</button>
<button id="remove">Remove Class</button>
</body>
</html>
同样,jQuery UI 扩展了现有的 jQuery 方法来添加功能。在这种情况下,增强的是addClass和removeClass方法。我在第八章中描述了这些方法的标准版本。jQuery UI 版本做了完全相同的事情:方法的第二个参数是 duration,jQuery UI 动画显示了从一个类到另一个类的转换。
在这个例子中,我定义了一个名为myClass的类,文档中有按钮使用fast的持续时间简写来添加和删除这个类。你可以在图 35-2 中看到效果。
图 35-2 。使用类制作元素动画
提示应用标准的 CSS 样式级联规则,这意味着只有当一个类对于一个或多个目标元素是最特定的时,该类中的属性才会被应用。在前一个例子中,我通过
id设置了元素的初始状态,但是在这个例子中,我使用了一个类,这样我的修改就生效了。CSS 样式层叠的细节见第三章。
jQuery UI 还增强了toggleClass方法——这与我在第八章中描述的标准toggleClass方法的工作方式相同,但是采用了一个持续时间参数并使过渡动画化,就像上面的addClass和removeClass示例一样。
切换类别
除了增强一些标准方法之外,jQuery UI 还定义了switchClass方法,该方法删除一个类并添加另一个类,以动画形式显示从一种状态到另一种状态的转换。清单 35-3 包含了一个演示。
清单 35-3 。使用 switchClass 方法
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
.elemClass {
border: medium solid black;
width: 200px; height: 50px;
text-align: center;
line-height: 50px;
display: block;
margin-bottom: 10px;
}
.classOne {
font-size: 25px; background-color: white; color: black;
}
.classTwo {
font-size: 40px; background-color: black; color: white;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("button").click(function() {
$("#animTarget").switchClass("classOne", "classTwo", "fast")
})
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div id=animTarget class="elemClass classOne">
Hello!
</div>
<button>Switch Class</button>
</body>
</html>
switchClass方法的参数是应该删除的类、应该添加的类和动画的持续时间。在这个例子中,我的两个类定义了相同的属性,但是这并不是必须的。
使用 jQuery UI 动画
jQuery UI 包括许多可以应用于元素的动画效果,就像第十章中的核心 jQuery 效果一样。我的建议是谨慎使用这些效果。精心制作的动画可以真正提升用户体验——但通常情况下,它们会成为用户烦恼和沮丧的来源。有许多不同的动画效果,包括blind、bounce、clip、drop、explode、fade、fold、highlight、puff、pulsate、scale、shake、size和slide。
注意在这一章中,我将向你展示如何应用这些效果,但我不打算深入每个单独效果的细节。在
http://docs.jquery.com/UI/Effects有一个很好的效果总结和可以应用到其中一些效果的设置。
使用效果来显示和隐藏元素
jQuery UI 增强了 jQuery UI 的show、hide,和toggle方法来应用动画效果。我在第十章中描述了这些方法的最初版本。要使用这些方法的增强 jQuery UI 版本,请提供附加参数,指定您想要使用的效果以及应用该效果的持续时间。清单 35-4 显示了这些增强方法的使用。
清单 35-4 。使用增强的显示、隐藏和切换方法
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
.elemClass {
font-size: 25px; background-color: white; color: black;
border: medium solid black; width: 200px; height: 50px;
text-align: center; line-height: 50px; display: block; margin-bottom: 10px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("button").click(function() {
switch (this.id) {
case "show":
$("#animTarget").show("fold", "fast");
break;
case "hide":
$("#animTarget").hide("fold", "fast");
break;
case "toggle":
$("#animTarget").toggle("fold", "fast");
break;
}
})
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<button id="hide">Hide</button>
<button id="show">Show</button>
<button id="toggle">Toggle</button>
<div id=animTarget class="elemClass">
Hello!
</div>
</body>
</html>
在这个例子中有三个按钮,点击它们会导致调用show、hide,或toggle方法。对于所有三个按钮,我已经指定了应该应用fold动画,使用fast持续时间。除了转换是动态的之外,这些方法的工作方式与核心 jQuery 类似。
应用独立效果
jQuery UI 定义了effect方法,它允许我们将动画应用到元素上,而不必显示或隐藏它。当使用正确的动画时,这是吸引用户注意文档中某个元素的有效方法。清单 35-5 包含了一个例子。
清单 35-5 。使用效果方法
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
.elemClass {
font-size: 25px; background-color: white; color: black;
border: medium solid black; width: 200px; height: 50px;
text-align: center; line-height: 50px; display: block; margin-bottom: 10px;
}
</style>
<script type="text/javascript">
$(document).ready(function() {
$("button").click(function() {
$("#animTarget").effect("pulsate", "fast")
})
});
</script>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div id=animTarget class="elemClass">
Hello!
</div>
<button>Effect</button>
</body>
</html>
当这个例子中的按钮被点击时,效果被就地应用,而不会对可见性有任何永久的改变。在这种情况下,我使用了pulsate效应,它导致元素脉冲打开和关闭。
使用 jQuery UI CSS 框架
jQuery UI 通过将一组类应用于应用一些复杂 CSS 样式的元素来管理小部件的外观。其中一些类向程序员公开,这样不属于小部件的元素就可以以一致的方式进行样式化——我在本书第四部分的例子中使用了其中一些类。
使用小部件容器类
CSS 框架中三个最基本的类应用了小部件上使用的核心样式。这些类别在表 35-3 中描述。
表 35-3 。jQuery UI 小部件容器类
| 班级 | 描述 |
|---|---|
ui-widget | 应用于所有容器元素。 |
ui-widget-header | 应用于标题容器元素。 |
ui-widget-content | 应用于内容容器元素。 |
这些类被应用于容器元素——也就是那些包含所有头和内容元素的元素(或者,在ui-widget的情况下,最外层的元素)。清单 35-6 展示了如何应用这些类。
清单 35-6 。使用 jQuery UI 小部件容器类
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
body > div {float: left; margin: 10px}
</style>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div>
<div>
Flowers
</div>
<div>
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
<div class="ui-widget">
<div class="ui-widget-header">
Flowers
</div>
<div class="ui-widget-content">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
</body>
</html>
在这个例子中有两组元素,其中一组我已经应用了容器类。你可以在图 35-3 中看到效果。
图 35-3 。应用 jQuery UI 小部件容器类
应用圆角
下一组 CSS 框架类让我们将圆角应用于类似小部件的元素。表 35-4 描述了这一类别中的等级。
表 35-4 。jQuery UI 小部件圆角类
| 班级 | 描述 |
|---|---|
ui-corner-all | 将元素的所有角变圆。 |
ui-corner-bl | 圆角左下角。 |
ui-corner-bottom | 使左下角和右下角变圆。 |
ui-corner-br | 倒圆角。 |
ui-corner-left | 使左上角和左下角变圆。 |
ui-corner-right | 倒圆角右上角和右下角。 |
ui-corner-tl | 圆角左上角。 |
ui-corner-top | 将左上角和右上角倒圆角。 |
ui-corner-tr | 圆角右上角。 |
这些类只有在元素有背景或边距时才有效,这意味着它们可以应用于ui-widget-header和ui-widget-content类,如清单 35-7 所示。
清单 35-7 。使用圆角类别
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
body > div {float: left; margin: 10px}
</style>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div>
<div>
Flowers
</div>
<div>
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
<div class="ui-widget">
<div class="ui-widget-headerui-corner-top"style="padding-left: 5px">
Flowers
</div>
<div class="ui-widget-contentui-corner-bottom">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
</body>
</html>
为了创造一个整体效果,我把 header 元素的顶角和 content 元素的底角弄圆了。你可以在图 35-4 中看到结果。请注意,我在 header 元素中添加了一些填充——圆角应用于元素的内容框中,这可能需要一些额外的空间来避免剪切内容。
图 35-4 。对元素应用圆角
使用交互状态类
您还可以应用 CSS 框架类来显示不同的交互状态,这允许创建以与 jQuery UI 小部件相同的方式响应用户交互的元素。表 35-5 描述了可用的等级。
表 35-5 。jQuery UI 交互类
| 班级 | 描述 |
|---|---|
ui-state-default | 应用可点击小工具的默认样式。 |
ui-state-hover | 应用鼠标悬停在可点击小工具上时使用的样式。 |
ui-state-focus | 应用可点击小工具获得焦点时使用的样式。 |
ui-state-active | 应用可点击小工具活动时使用的样式。 |
清单 35-8 应用了这四个类。请注意,在每种情况下,我都将填充应用于内部的span元素。交互状态类定义填充值,在容器元素和内容之间创建间距的最简单方法是将内部元素作为目标。
清单 35-8 。应用交互状态类
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
body > div {float: left; margin: 10px}
span {padding: 10px; display: block}
</style>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div class="ui-widgetui-state-defaultui-corner-all">
<span>Default</span>
</div>
<div class="ui-widgetui-state-hoverui-corner-all">
<span>Hover</span>
</div>
<div class="ui-widgetui-state-focusui-corner-all">
<span>Focus</span>
</div>
<div class="ui-widgetui-state-activeui-corner-all">
<span>Active</span>
</div>
</body>
</html>
你可以在图 35-5 中看到每个职业的效果。其中一些状态在我正在使用的 jQuery UI 主题中是相似的,但是如果需要的话,你可以使用 ThemeRoller(在第十七章中描述)来创建一个增加了状态强调的主题。
图 35-5 。交互状态类的效果
使用提示类
一些 CSS 框架类允许我们向用户提供关于文档中元素状态的提示。这些类别在表 35-6 中描述。
表 35-6 。jQuery UI 交互提示类
| 班级 | 描述 |
|---|---|
ui-state-highlight | 突出显示一个元素以吸引用户的注意。 |
ui-state-error | 强调包含错误信息的元素。 |
ui-state-disabled | 将禁用的样式应用于元素(但实际上并不禁用元素本身)。 |
清单 35-9 显示了高亮和禁用提示的使用。
清单 35-9 。使用 jQuery UI 高亮显示类
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
body > div {float: left; margin: 10px}
span {padding: 10px; display: block}
</style>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div class="ui-widget">
<div class="ui-widget-header ui-corner-top" style="padding-left: 5px">
Flowers
</div>
<div class="ui-widget-content ui-corner-bottom">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
<div class="ui-widgetui-state-highlight ui-corner-all">
<div class="ui-widget-header ui-corner-top" style="padding-left: 5px">
Flowers
</div>
<div class="ui-widget-content ui-corner-bottom">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
<div class="ui-widgetui-state-disabled">
<div class="ui-widget-header ui-corner-top" style="padding-left: 5px">
Flowers
</div>
<div class="ui-widget-content ui-corner-bottom">
<div class="dcell">
<img src="peony.png"/><label for="peony">Peony:</label>
<input name="peony" value="0" />
</div>
</div>
</div>
</body>
</html>
你可以在图 35-6 中看到这些类的效果。注意,在使用ui-state-highlight类时,我也应用了ui-corner-all样式。这个类应用了一个边框,默认情况下显示为方形的角。如果子元素有圆角,那么您也需要将突出显示的元素圆角化。
图 35-6 。应用突出显示提示类
清单 35-10 显示了错误状态的使用。
清单 35-10 。使用错误提示
<!DOCTYPE html>
<html>
<head>
<title>Example</title>
<script src="jquery-2.0.2.js" type="text/javascript"></script>
<script src="jquery-ui-1.10.3.custom.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="styles.css"/>
<link rel="stylesheet" type="text/css" href="jquery-ui-1.10.3.custom.css"/>
<style type="text/css">
body > div {float: left; margin: 10px; padding: 20px}
</style>
</head>
<body>
<h1>Jacqui's Flower Shop</h1>
<div class="ui-state-error">
Oops! Something went wrong.
</div>
</body>
</html>
你可以在图 35-7 中看到效果。
图 35-7 。使用错误提示类
摘要
在这一章中,我描述了 jQuery UI 为颜色、可见性的动画转换提供的增强。和 CSS 类。这些都是有用的特性,但是必须小心使用,避免给用户带来干扰和恼人的效果。我还描述了 jQuery UI CSs 框架的主要类,它允许我们以与 jQuery UI 小部件一致的方式设计元素,允许我们将 jQuery UI 主题的外观扩展到 HTML 文档的其余部分。