天天看点

JavaScript高级编程 III

    在本系列的终结篇中,我们继续学习另外一个有用的JavaScript案例,主要是通过DOM随意操作和重写HTML页面。和以前一样,本文中使用JavaScript应该不经修改就可以在当前所有主要的浏览器上使用。

        使用你的鼠标进行魔术变换(Magic Mutating with Your Mouse)

         在创建表单时,你有时可能希望根据某些次级条件改变输入域的类型。例如,假设你正在编写一个图书馆的搜索界面。如果通过作者搜索,你可能想使用一个标准文本框。如果通过图书类型(比方说精装本或普通),你可能想提供一个下拉列表;以及如果使用图书内容搜索,就提供一个大文本域。试试下面的,你就会比较清楚了:

Search for: AuthorBindingContent

      JavaScript通过一个回调函数实现这个功能,只要改变搜索类型的值,回调函数就会被触发。  

表单的布局代码为:

        这里使用技术其实很简单。无论何时用户改变searchtype的值(通过从下拉列表中选择),onchange属性就会调用我们的JavaScript函数,UpdateSearchField,将刚刚选择的值和表单中搜索框的id作为参数。该函数创建一段HTML代码,然后使用新代码替换当前的搜索框。很显著,我们正在随意重写网页的部分代码。

        然后,我们复制input(它的id已经作为传入参数--该节点最终会被我们的新节点替代)的部分或者所有属性到新节点中,此时用到了助手函数CopyAttributes。你可能需要改变这个函数以适应你的需求。例如,你可能想要复制对象类或者对象值。复制以后,节点的属性就设置好了。

       对于SELECT元素,我们就需要多一点工作了,需要为它创建OPTION。这个在一个循环中完成,和创建其他元素的方式一样,使用createElement和createTextNode。后面的工作和前面一样,只不过不是创建HTML标签,而是创建一个包含纯文本的DOM节点。它是包含在HTML的OPTION标签之间的文本。然后,使用DOM函数appendChild将这些option插入到SELECT标签中间。如果一步步地看for循环中的过程,这个过程可能更加容易理解。

循环次数

生成的HTML

<SELECT>

</SELECT>

1

<OPTION value="HardCover">HardCover</OPTION>

2

<OPTION value="Paperback">Paperback</OPTION>

3

<OPTION value="Magazine">Magazine</OPTION>

     最后,我们新的searchbox节点终于创建完毕,我们需要将它放在页面的某个位置。同时需要删除现在的searchbox。很方便地,replaceChild函数就是用来满足我们需要的,使用我们创建的新节点替换已存在的节点(以及它的子节点,如果有的话)。这样,我们创建的新HTML就替换了已有的id为searchbox的HTML代码块。至此,大功告成。

      当然,这种技巧并不局限于搜索表单中的INPUT框,实际上通过页面上某些事件的触发,你可以将任何元素变为任何其他的元素。增加一点想象力,许多有趣和令人印象深刻效果就可以通过该技术实现。

动态表格(Dynamic Tables)

     在用户界面上经常期望用户可以输入不确定函数的数据。例如,当填写订单时,用户会一行输入一条记录。有了JavaScript,我们再也不用担心是否为订单预留了足够的控件,让用户根据需要添加和删除行就可以了。下面是一个即时的演示,在上个例子的基础上更近一步。

Catalog #

Description

Quantity

Unit Price

Total

<b>Grand Total:</b>

        要在最后增加一行,只需要Append Row按钮。要删除一行,就点击行尾的红色“X”。当只剩下最后一行时就不能删除了。另外注意,只要你输入了数量和价格或者删除了一行,Total和Grand Total的值就会更新。这些是如何实现的呢?首先,我们使用HTML创建一个初始的表格。注意,初始时必须至少有一行。

    这只是一个由表单包围的标准HTML表格,其每一个单元格都包含一个供用户输入数据的input标签。注意,每一个input标签的name和id值的都是以“_1”结尾,删除按钮的id亦是如此。它用来保证标签的唯一性,同时表明标签属于哪一行——当前就是第一行。例如,第3行中price列的name和id就是price_3,等等。Total和Grand Total域是不可用的,因为它们是计算列,是只读的。所有的任务都有三个JavaScript函数完成:AppendRow与同名按钮相关联,DeleteRow与删除X关联,以及UpdateTotal,只要在Quantity或者Price域按下某个键就会被触发,计算总和。首先来看第一个函数:

      这个函数使用表格id作为唯一的输入参数,计算表单的总数和总数之和。它首先计算行数,然后在其上循环。注意,第1行(下标为0)是表格的头,所以我们跳过它从第2行(下标为1)开始。它读取每一行的数量和价格信息,这里就利用了这些列id的命名规则;接着,两者值都不为0,则将其相乘计算总数。计算结果保存到当前行的Total列中。总数之和也被计算出来放在Grand Total域中。注意,尽管它们处于不可用状态,我们仍然可以使用JavaScript函数对其进行赋值。现在,我们来看AppendRow函数:

    再次,表格的id是唯一的输入参数。我们获取DOM树中表格的第一个tr的句柄放在变量row中,然后检查其父节点以获取表格中总行数。接着,我们使用DOM函数cloneNode创建一个当前行的拷贝。这个函数的输入参数表示我们最好还是包含所有子节点,所以我们获得整个行的拷贝,而不是一个空的tr节点。助手函数,rowrenumber,用来根据相应的行数(它是表单可输入行的总数加1)修改所有id。DOM函数appendChild将新行添加到表格中。最后,我们把输入域,因为它们复制于第一行——我们只想复制结构,而不复制数据。X被放在第1行的删除列中,新行中也是(当只剩下一行时它就被空白替代,这些用户就不会尝试删除了)。我们来简单看一下重新编号id的函数:

     这个从以变量newrow传入的tr节点开始遍历DOM树。它完成两个级别的遍历,查找所有没有id的域。我们只做两个级别深入(例如,for循环的两个嵌套),因为我们知道在表格中这些id在什么地方。第一级是td标签,第二级是input标签,也就是我们的所有id所在的地方。因为我们对id采用了特别的命名方{type}_{rownumber},现在我们就可以利用它,使用JavaScript的split函数将字符串分开,然后使用新的行号替换旧的。我们同时更新name域,如果存在的话,以及第一列的tabIndex。

     表单的最后一部分是删除行的函数:

     在HTML中,这个函数在span标签中被调用,以this作为输入参数。因此进入该函数,el就是被点击的span标签的指针,并且我们必须向上走两级找到DOM树中的tr节点。我们也可以使用this.parentNode.parentNode作为输入参数。首先,我们必须保证至少有两行,因为只剩下一行时是不允许删除的。然后,我们使用DOM函数removeChild删除行节点。注意,它是作为父元素tbody节点的方法被调用的。

     接下来,我们遍历表格树在每一个tr节点上停止。在被删除行以后的行,如果有的话,都需要重新编号,因为它们行号已经减少了1(例如,删除行4意味着行5变成行4,行6变成行5,等等,而行1、2和3保持不变)。我们使用方便的rowrenumber函数完成这个任务,这个函数在前面已经讨论过。然后,我们停下来检查是不是只剩下一行(也就是说,在删除的行前要有两行)。如果是这样,我们使用空白替换X来隐藏删除按钮,以使用户不会尝试删除它(但是如果用户不知何故地要删除,什么都不会发生,因为在上面我们已经对这种情况做了检查)。最后,删除一行意味着Grand Total的值可能改变,除非删除行为空,所以我们调用UpdateTotals保证计算结果保持一致。

      这可能是到现在为止我们看到的最复杂的例子了,但是如果你将它拆分为不同的组成部分,你会发现其实非常简单。这个例子大量利用了DOM函数和DOM树,并且很有希望能让你对按照自己的意愿动态操作HTML页面有一个初步认识。作为练习,尝试为每一行增加一列,该列包含一个Insert Row按钮,点击按钮可以在当前行之前插入一空白行。提示:应该只需对AppendRow函数做一下简单调整就可以实现。

Elusive Text

    在这个最后的例子中,仍然是使用DOM,我们将学习一个简单的方法,用来在输入域中显示临时的文本,这个文本在输入域不完全为空时消失。这个方法可以用来节省空间,例如,直接把输入域的标题放在输入框内而不是左边和上面。下面就是一个可以运行的例子:

Enter Login Name    Enter Password   

    创建这个简单的页面的HTML代码如下所示:

    首先我们为浮动文本定义CSS样式。绝对定位用来让文本位于页面上其他元素的最上层(这里是输入框)。同样,z-index设为high用来保证文本出现在最上层,而不会隐藏到其他元素后面。overflow设置为hidden,因为我们不想在文本周围出现滚动条。如果它不能正好符合空白,将会被截断。

   这个表单是一个标准的表单,和函数UpdateHelpText相关连,在输入域中有键被按下或释放,或者鼠标被释放时。这个函数,如我们将要看到的,根据输入区域是否为空来隐藏或者显示文本。因此,只要我们开始在输入框中键入信息,浮动文本就会消失。当我们完全删除它的内容是,它又会魔术般重现。我们在表单之后就调用这个函数,初始化文本并把它正确定位在表单内。在每一个input标签之后的是div标签,包含了文本,利用了我们上面创建的helptext CSS类。注意,因为我们设置z-index比较高使文本在最上层,现在就有了一个问题,点击文本时就选中了这个文本而不是移动光标到了输入区域,而后者可能正是用户想要的。为了修正这个问题,在鼠标点击帮助文本时我们调用一个函数ChangeFocus。下面我们看一下这两个JavaScript函数:

   ChangeFocus相当简单。它从当前节点开始(div标签),在DOM树中回溯直到找到第一个input标签。在input标签上调用focus将文本光标送到输入域,就好像用户直接点击了input区域。结果就是点击浮动文本就好像直接点击了输入区域。注意,我们可以使用id将div标签和相应的input标签连接起来——例如,input标签的id为login,div的id为login_label,我们就可以很容易从一个节点找到另外一个节点——但是,我们使用HTML页面结构的知识来找到节点。如果我们使用id,那么关于页面结构的知识和假设就没有必要了,因此对于一般的应用程序它可能不失为一种较好的方法。

总结(Summary)

    在这一部分,我们探讨了一些使用JavaScript操作DOM的技巧。通过这些技巧,开发者可以修改HTML页面的任何部分,可以增加原来页面没有的内容,删除内容,甚至可以四处拖拽。这些例子只不过是浅尝辄止,一点想象力就可以实现。有一点很重要,需要指出,尽管现在多数浏览器都允许重写计算机内存中的HTML页面,如果你做了过多的更改,事情可能变得有点使人不太愉快,有些浏览器对这种情况的处理比较好。特别地,像尝试使用回调函数创建新节点的情况在输入变量上可能有些不太容易把握,这种情况在IE和Mozilla家族的浏览器之间的处理方法就大相径庭。我希望你已经发现这几篇文章的意义,也希望他们可以激发您的灵感,使用一小段的高级JavaScript让你网站更加生动有趣。

继续阅读