在 Forge - JavaScript

作者:Reuven M. Lerner

大约 18 个月前,Web 开发者开始谈论 Ajax。不,我们不是突然对擦洗厨房台面感兴趣;相反,我们正在寻求利用 Web 开发的最新范式。这项技术本身并不是全新的,但它是一个 Web 开发者可以采用的通用范式的想法是新的——当 James Paul Garrett 赋予它一个响亮的术语 Ajax(异步 JavaScript + XML 的缩写)时,每个人都开始要求 Ajax Web 应用程序只是时间问题。

Ajax 背后的理论相对简单。JavaScript 擅长动态更改 Web 浏览器显示的 HTML 页面。现代版本的 JavaScript 能够向服务器发出异步 HTTP 请求。如果我们结合这两个特性,我们就可以在浏览器中创建类似桌面的应用程序。突然之间,Web 成为各种新应用程序的稳定平台。Google 地图只是一个开始;准备迎接 Ajax 文字处理器、电子表格、网络管理程序、绘图程序——你能想到的都有。

事实上,在过去一年左右的时间里,我们已经看到了 Ajax 应用程序的爆炸式增长。为创建现有应用程序的 Ajax 版本而成立的初创公司已经被谷歌等公司收购。现有的网站正在争先恐后地加入 Ajax 功能。图书出版商正在像印刷过时的书籍一样印刷 Ajax 相关书籍。我可能至少知道六个用于向应用程序添加 Ajax 的工具包,而且新的工具包还在不断发布。

Ajax 背后的许多兴奋之处在于它给予设计师和开发者的自由。在 Ajax 之前,Web 应用程序可能看起来很漂亮,但它们基于页面的界面让人想起旧式大型机,其应用程序在页面模型上运行。什么,你想创建一个增量更新的应用程序?抱歉,HTTP/HTML 组合意味着你要么获得一个新页面,并享受它提供的功能,要么你停留在当前页面。每次页面更新都必须伴随一个 HTTP 请求,反之亦然。

毫无疑问,Ajax 应用程序比旧式 Web 应用程序具有更简洁的外观和感觉。它们感觉更自然、更灵敏,很容易想象所有 Web 应用程序在几年内都会变成这样。这可能总体来说是一件好事,我期待着未来会带来什么。事实上,我猜想在几年内,说你是“Ajax 开发者”听起来会像说你是“cookie 开发者”,或者“DOM 开发者”,甚至“数据库开发者”一样可笑。正如现在理解这些技术中的每一项都是成为 Web 开发者所期望的一部分一样,Ajax 也是如此。是的,这意味着 Web 开发者如果想跟上时代,还需要学习另一套技术。

从本月开始,我将开始研究 Ajax,重点关注你为了适应这种新范式所需要了解的核心内容。我希望涵盖整个堆栈,从 Ajax 所依赖的基础设施——JavaScript、动态 HTML 和 XML——开始,向上移动到 Ajax 技术本身,最后研究提供预打包 Ajax 功能的库和框架。

本月的专栏从 JavaScript 语言开始,它位于每个主要的 Web 浏览器中。在本专栏结束时,你应该对如何在 JavaScript 中编写基本函数以及如何确保它们在各种不同情况下自动调用有一个很好的了解。

JavaScript

作为一种语言,JavaScript 有着悠久而曲折的历史。它最初是一种名为 LiveScript 的语言,它被放置在 Netscape 的浏览器中,以便将它们与竞争对手区分开来。当 Sun Microsystems 发布其 Java 语言,包括位于浏览器内部的小程序时,名称更改为 JavaScript。对 Java 小程序的巨大热情说服 Netscape 将名称更改为 JavaScript,从而引起了极大的混乱。然后,微软在 Internet Explorer 中发布了一个与 JavaScript 大致兼容的版本,称为 JScript。

在 Web 开发者处理了几年不兼容的版本之后,Netscape 让欧洲组织 ECMA 将该语言标准化。官方来说,该语言现在被称为 ECMAScript,但没有人真正这样称呼它。Internet Explorer 和 Mozilla 中的版本现在在很大程度上与标准兼容,尽管仍然存在需要解决的差异和问题。

尽管 JavaScript 偶尔在其他地方使用,但它主要是一种位于 Web 浏览器内部的语言。它的优势在于它允许 Web 开发者将静态 HTML 页面转换为交互式应用程序。JavaScript 可以控制 Web 页面的每个方面,包括内容、表单和设计。JavaScript 通过 DOM(文档对象模型,万维网联盟描述 HTML 和 XML 文档的标准)和 BOM(浏览器对象模型,一种非标准化的获取有关浏览器及其对象(如窗口)信息的方式)访问此信息。

我现在应该坦率地表明,我并不特别喜欢 JavaScript 语言。尽管自从它首次发布以来,它在各个方面都得到了显着改进,但一些方面仍然让我感到困扰,包括对象模型和可以表示未定义值的多种方式。话虽如此,不喜欢 JavaScript 并不能改变它位于 Ajax 开发核心的事实。忽略 JavaScript,或者希望它会简单地消失甚至被另一种语言取代的 Web 开发者是不现实的。相反,我们应该学习 JavaScript,理解它的优点和缺点,然后在我们的应用程序中尽可能明智地使用它。

JavaScript 通常放在 HTML 文档中的 <script> 标签内。理论上,我们可以将 JavaScript 放在任何类型的 HTML 文档中,忽略标准。在实践中,当以任何严肃的方式使用 JavaScript 和其他 Ajax 技术时,明智的做法是使用 XHTML,即 HTML 的 XML 兼容版本。这提高了我们预测页面是否能与 JavaScript 正确工作以及用户浏览器如何呈现页面的能力。因此,一个包含 JavaScript 的简单页面可能如下所示

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
    function helloWorld() {
        alert("Hello, world!");
    }
    </script>
    <title>Test JavaScript page</title>
</head>
<body>
    <p>Hi there!</p>
</body>
</html>

页面的 HTML 部分你可能很熟悉。在 <script> 和 </script> 标签之间,我们定义了一个 JavaScript 函数 helloWorld,它弹出一个包含我们文本的模态对话框。上面的页面工作正常,但 JavaScript 本身永远不会执行。我们如何才能让它触发?

也许最简单的方法是创建一个按钮——一种专门为此任务设计的 <input> 类型——然后让该按钮执行我们的代码。例如

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
    function helloWorld() {
        alert("Hello, world!");
    }
    </script>
    <title>Test JavaScript page</title>
</head>
<body>
    <p>Hi there!</p>

    <p><input type="button" value="Click me!"
        onclick="helloWorld();" /></p>
</body>
</html>

关于前面的代码,有几点需要注意。首先,我们设法通过事件处理程序将函数 helloWorld 连接到我们的按钮。这是 JavaScript 中执行大多数函数的方式,也是我最喜欢的语言部分之一。你在 <script> 部分中定义你的函数,然后使用适当的事件处理程序指示它们应该何时触发。

有各种各样的处理程序,都以字母“on”开头,因此你可以在某人单击 (onclick)、某物发生更改 (onchange)、元素获得鼠标焦点 (onfocus) 或失去焦点 (onblur) 以及许多其他可能性时执行函数。(因为我们正在使用 XHTML,所以所有属性都必须是小写的。因此,尽管通过编写 onClick 使事件处理程序更易读可能很诱人,但这会使页面无效。)

我们可以通过个性化消息使它更有趣

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
    function helloWorld() {

        var username = document.getElementById("username").value;

        if (username == "")
        {
        alert("Please enter your name in the text field.");
        }
        else
        {
        alert("Hello, " + username + "!");
        }
    }
    </script>
    <title>Test JavaScript page</title>
</head>
<body>
    <p>Hi there!</p>

    <p>Enter your name: <input type="text" id="username" /></p>

    <p><input type="button" value="Click me!"
        onclick="helloWorld();" /></p>

</body>
</html>

在这个最新版本中,我们向文档添加了一个文本字段(类型为 text 的 <input> 标签)。文本字段有一个 id 属性,它在 HTML 页面上唯一标识它。同时,我们的 JavaScript 函数变得有点复杂。我们定义了一个 username 变量,用 var 关键字指示它是一个局部变量,而不是全局变量。然后我们要求 JavaScript 在当前文档中为我们提供 username 元素的值。由于 id 属性是唯一的,我们知道页面上最多只有一个 ID 为 username 的元素。但是,我们可能会拼错或以其他方式弄错 username。或者用户可能尚未在文本字段中输入他们的姓名,认为他们应该立即单击按钮。无论如何,我们需要测试 username 变量的值,这样我们就不会愚蠢地说“Hello, !”给用户。因此,我们使用 if 语句,它与其他许多类 C 语言中的 if 语句类似,如果有人试图在没有输入用户名的情况下说“hello”,则会弹出一个警告提示。

修改页面

到目前为止,我们已经看到 JavaScript 使读取页面中的值变得容易。但 JavaScript 的部分魔力,以及它成为 Ajax 核心的原因,是我们可以像读取页面内容一样轻松地修改页面的内容。例如

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
    function helloWorld() {

        var username = document.getElementById("username").value;

        if (username == "")
        {
        alert("Please enter your name in the text field.");
        }
        else
        {
        alert("Hello, " + username + "!");

        var result = document.getElementById("result")
        var status_text =
            document.createTextNode("Thanks for clicking!");
        result.appendChild(status_text);
        }
    }
    </script>
    <title>Test JavaScript page</title>
</head>
<body>
    <p>Hi there!</p>

    <p>Enter your name: <input type="text" id="username" /></p>

    <p><input type="button" value="Click me!"
        onclick="helloWorld();" /></p>

    <p id="result"></p>

</body>
</html>

此版本与上一个版本之间的区别在于 if 语句的 else 子句。在显示“hello”警告框后,我们查找 ID 为 result 的元素。这属于页面上的一个新的 <p> 元素,其内容当前为空,并且位于按钮正下方。请注意我们两次调用 getElementById 的区别。在 username 的情况下,我们想要用户输入的文本,因此在返回的节点上调用 value 方法。在 result 的情况下,我们将节点本身保留在变量中,而不获取任何值或其他属性。那是因为我们想要修改 result 节点,添加一个包含我们消息的文本节点。

事实上,我们通过调用 document.createTextNode 创建新的文本节点,并向其传递我们想要显示的文本参数。创建文本节点不会显示它;为了将它放在页面上,我们必须将它附加到已经显示的节点。我们将使用 appendChild 方法将其附加到我们的 result 节点。

现在,每次我们单击“点我!”按钮时,我们不仅会收到警报,还会修改页面的内容。

但是,上面的代码存在一个问题。因为我们使用 appendChild 方法来添加我们的文本节点,所以我们最终会为每次单击按钮创建并附加一个新的文本节点。例如,在单击按钮十次后,我们将在页面上看到我们的“感谢点击”消息显示十次。

避免这种情况的最简单方法是询问 result 节点是否具有任何子节点。如果有,我们可以假设我们已经显示了我们的结果。如果不存在子节点,我们可以安全地继续添加新的文本节点

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <script type="text/javascript">
    function helloWorld() {

        var username = document.getElementById("username").value;

        if (username == "")
        {
        alert("Please enter your name in the text field.");
        }
        else
        {
        alert("Hello, " + username + "!");

        var result = document.getElementById("result")

        if (result.childNodes.length > 0)
        {
            alert("Already has children; not adding to the message");
        }
        else
        {
            var status_text =
            document.createTextNode("Thanks for clicking!");
            result.appendChild(status_text);
        }
        }
    }
    </script>
    <title>Test JavaScript page</title>
</head>
<body>
    <p>Hi there!</p>

    <p>Enter your name: <input type="text" id="username" /></p>

    <p><input type="button" value="Click me!"
        onclick="helloWorld();" /></p>

    <p id="result"></p>

</body>
</html>

上面的最终版本的 HTML 页面在检索 result 节点后添加了一个新的 if 语句。正如我们可以向 result 添加新节点一样,我们也可以查询它以查看它是否已经有子节点。当它首次下载到用户的浏览器时,result 节点没有子节点。它是空的。因此,我们可以调用 result.childNodes,它返回 result 节点的所有子节点的列表。

在这种情况下,当我们只想检查是否存在任何子节点时,我们在返回的节点列表上调用 length 方法。如果存在任何数量的子节点,我们会警告用户我们不会添加到消息中。我们将修改 HTML 页面一次,添加我们的“感谢点击”消息,但如果用户再次单击,JavaScript 将检测到现有的文本节点并且不会进行更改。

结论

JavaScript 是向 Web 上的 Ajax 应用程序转变的核心,因此对于 Web 开发者来说,理解它是一项必不可少的技术。JavaScript 使编写读取和写入服务器返回的 HTML 的函数成为可能。然后通过将这些函数附加到预定义的事件来调用它们。下个月,我们将看到 JavaScript 如何允许我们使用样式表,样式表会影响页面的设计。

Reuven M. Lerner,一位长期的 Web/数据库顾问,是伊利诺伊州埃文斯顿西北大学学习科学专业的博士候选人。他目前与妻子和三个孩子住在伊利诺伊州斯科基。你可以在他的 Weblog 上阅读他的文章:altneuland.lerner.co.il

加载 Disqus 评论