第一页:Javascript高级教程 - 第三课
事件的定时以及浏览器的识别
随着我们所掌握的JavaScript日渐丰富,我们可以编制出越来
越复杂的应用程序。上次的课程中我们学习了如何利
用JavaScripts编写Cookies。今天我们将学习如何加入定时
功能。给时间定时的能力在3.0版的浏览器中非常有用,而在更
新的浏览器中则会在动态HTML应用中发挥更大的功用。
如果你用的是Netscape 4.0或微软的IE 4.0,你可能回注意到
动态HTML可以在页面中移动不同的内容。在JavaScript中将某
个物体在屏幕中移动需要用到一个循环(loop),用循环的功
能使物体随着时间推移移动。但是你如何告诉JavaScript将某
个物体每秒钟或者每10分之一秒移动一点呢?
然而在不同的浏览器中将某物体进行移动的能力有很大的差别。
要想在不同的浏览器中都可以应用动态HTML的东西,你需要做
一些特别的处理。所以在讲解完事件定时之后我将讲解如何识
别用户所使用的是何种浏览器(针对不同的浏览器你可以使用
不同的程序代码)。
先学习事件的定时。点击下面的按钮,3秒钟后将弹出一个提
示框。
第二页:如何给事件定时
在Javascript给事件定时很容易。其关键指令是setTimeout()
和clearTimeout()方法。利用setTimeout(), 指令可以在未来
的某个指定时间执行特定指令。如果你改变主意,你可以用
clearTimeout()取消setTimeout的定时.
下面是setTimeout的基本格式:
var the_timeout = setTimeout("some javascript
statement", some_number_of_milliseconds);
在上面的例子中用到的指令如下:
var the_timeout = setTimeout("alertAndRedirect ();",3000);
该语句中有3项重要内容:
setTimeout返回一个值。在该语句中
the_timeout是一个变量,它指向特定的setTimeout。
如果你想取消该setTimeout的定时,你只需引用该变量
即可。你可以给变量起一个别的名称。
在setTimeout中用到的第1个变量是一个JavaScript 语句的字
符串。
在本例中第1个参数是字符串:"alertAndRedirect();"
alertAndRedirect是为载入一个提示框而编写的一个函
数,当用户点击“OK”时还重新回到本页面。
注意在引号中的内容是一个完整的JavaScript语句,带有
分号和其他必须的语法. 如果你执行该段代码则会调用函
数alertAndRedirect。setTimeout只规定了该语句出现的
时间。
以下是函数alertAndRedirect()的代码:
function alertAndRedirect()
{
alert('ok! exhale!');
window.location.replace("timing.htm");
}
setTimeout的第2个参数指定了在多少毫秒之后执行第1个参数。
一秒钟等于1000毫秒。所以如果你想让某事在秒钟之后发
生你必须将第2个参数设置为3,000 毫秒。
我们可以做下面的练习熟悉它的工作原理:
第三页:定时循环的概念
其制作方法就是让按钮的onClick事件调用以下函数:
function ringBell()
{
var timer1 = setTimeout("window.document.the_form.the_text.value='3 seconds!';",3000);
var timer2 = setTimeout("window.document.the_form.the_text.value='6 seconds!';",6000);
var timer3 = setTimeout("window.document.the_form.the_text.value='9 seconds!';",9000);
}
它的意思是,“从现在开始,三秒钟后显示‘三秒’,六秒钟
后显示‘六秒’,九秒钟后显示‘九秒’”,很好理解,对吧?
但是,下面这样却不行:
function doDumbTimer()
{var timer1 = setTimeout("window.document.the_form.the_text.value='3 seconds!';",3000);
var timer2 = setTimeout("window.document.the_form.the_text.value='6 seconds!';",3000);
var timer3 = setTimeout("window.document.the_form.the_text.value='9 seconds!';",3000);
}
试一下这个错误的定时代码看看会发生什么?
请注意当你等了三秒钟,三个定时信息之一神秘地出现在文
本框里,然后就停在那儿。在上面的不好的代码中,每个
setTimeout都连续地执行,(就是“从现在开始,三秒钟
后显示‘三秒’,三秒钟后显示‘六秒’,三秒钟后显示
‘九秒’”)。所以当三秒钟以后,三件事儿都发生了,你
得到的正好是其中最后发生的结果----当然不是你希望的
结果。
一旦你理解了,setTimeout()还是相当容易使用的。但是
一个难点儿的问题提出来了:你如何去做一个定时器,让某
件事每隔2秒钟就发生一次,从现在一直到永远?象这个例
子:
先别担心停止定时器按钮,稍后我会讲clearTimeouts。只
要想想你怎么能够让定时器无限循环。实际上这是一个非常
重要的问题而不仅仅是一个小练习。就象我前边提到的那样,
当你用动态HTML让什么东西缓缓地在屏幕上移动时,就执行
一个定时循环:“轻轻移动一点,等待,再移动一点,再等
待.....如此这般”
你想到这个问题了吗?
好,答案并非那么简单。你无法象上面的那个例子那样,只
用一个函数,就能够每隔两秒就改变文本框的内容,象这样:
function theTimer()
{
var timer1 = setTimeout("changeTextBoxTo(2);",2000);
var timer2 = setTimeout("changeTextBoxTo(4);",4000);
var timer3 = setTimeout("changeTextBoxTo(6);",6000);
var timer4 = setTimeout("changeTextBoxTo(8);",8000);
var timer5 = setTimeout("changeTextBoxTo(10);",10000);
.
.
.
}
因为,好,你可以看出为什么不行:如果你想用这种方法让
某件事无限循环下去,你必须有无限多行的代码。相比起其
它问题,比如敲得你肩酸背痛来说,光是下载一个包含了无
限多行javascript的页面就需要太长的时间,所以,这种方
法根本就谈不上是一种选择。
这个也不行,虽然它看起来更酷一些:
function theTimer()
{
the_time = 0;
hellIsHot = true;
while (hellIsHot == true)
{
the_time += 2;
var timer = setTimeout("changeTextBoxTo(the_time);", the_time*1000);
}
}
请把程序研究一会,看看会得到什么结果。但不要尝试去运
行它,否则结果会使你很不愉快。让我们在“while"循环中
走几趟:
第一遍
while (hellIsHot == true) : 是的地狱还
是热的。
the_time += 2 : 所以现在 the_time = 2
var time = setTimeout("changeTextBoxTo(2);", 2000) :
所以, 从现在开始两秒后, 文本框变成了“2.",这正是我们想要的结果。
第二遍
while (hellIsHot == true) : 确实,地狱还是热的.
.
the_time += 2 : 所以现在 the_time = 4
var time = setTimeout("changeTextBoxTo(4);", 4000) :
所以, 从现在开始四秒后, 文本框变成了“4.",很好。
第三遍
while (hellIsHot == true) : 不, 地狱一
点也没凉快.
the_time += 2 : 所以现在 the_time = 6
var time = setTimeout("changeTextBoxTo(6);", 6000) :
所以, 从现在开始六秒后,
文本框变成了“6.",好。
第四遍
while (hellIsHot == true) : 是的,还是
热的。
还那样
还那样
你看明白了。这段代码看起来象是做对了。不幸的是其实不
是这样。相反它创建了一个死循环,一直设定setTimeouts
直到地狱冷下来。这里有两个问题。首先,当在循环里时,
你的浏览器就无法做任何其它的事情,基本上就是停止,执
行动作,再设定下一个定时器,一直到永远。第二,每次设
定setTimeout时,浏览器 都要记住你预定执行的内容以及
何时执行。最终你的浏览器会把内存耗尽,这时你的浏览器
会崩溃,或者你的计算机会崩溃,或者把你弄疯而永远也不
想再写一行Javascript程序了。
一点都不好
幸运的是,有一种方法能够写出成功的定时器循环。
第四页:定时循环的做法
要使定时器循环工作你需要写一个函数实现循环调用。这里是
一个例子:
var the_count = 0;
var the_timeout;
function doTimer()
{
window.document.timer_form.the_text.value = the_count;
the_count += 2;
the_timeout = setTimeout("doTimer();", 2000);
}
这里用到的定时器就是上一页所用的定时器。当用户点击按钮
时就调用该函数。该函数将the_count的当前值写到文字框中。
然后the_count增加2,则开始调用函数自身。文字框中的数值
也相应更新,the_count在增加2, 则再次调用函数自身。在等
待的这两秒期间浏览器则可以执行其他的同步工作。the_count
一增加2就执行另一次setTimeout()。你不用担心会造成内存
崩溃,因为在给定时间内只有一个setTimeout()在执行。
而无限的"while" 循环则会锁定浏览器的工作,在执行该循环
的过程中浏览器不能同时执行别的任何指令。而setTimeout则
可以使循环的间隙中让浏览器执行别的工作。
如何取消setTimeout
现在你已经学习了如何设置一个无限循环。但是你必须懂得如
何终止循环。其指令就是clearTimeout。上例中定时器还有下
面这个表单元素:
<input type="button" value="stop timer"
onClick="clearTimeout(the_timeout);">
点击这个按钮就可以终止定时器。指令是clearTimeout(),其
实很简单,如果你这样设置setTimeout,
the_timeout = setTimeout("some javascript",3000);
你可以这样取消定时器:
clearTimeout(the_timeout);
很简单,对吧?下面我们看一个复杂循环定时器,一个可以报
告时间的定时器。
第五页:一个Javascript编写的时钟
现在时间是:
点击“启动时钟”则时钟开始运行。它从你的计算机中读取时间
并每半秒更新一次文字框中的显示。这个例子通过一个自调用
的函数设置了一个定时器。同时这个例子可以让你了解一点Date
对象的功能。当讲解cookies时,我提到过Date对象。
以下是代码:
function writeTime() {
// 获得日期对象
var today = new Date();
// 从对象中获得信息
var hours = today.getHours();
var minutes = today.getMinutes();
var seconds = today.getSeconds();
// fixTime 使分和秒可以正常显示
// 对于小于10的数字则在该数字前加一个0
minutes = fixTime(minutes);
seconds = fixTime(seconds);
//将时间字符串组合在一起并写出
var the_time = hours + ":" + minutes + ":" + seconds;
window.document.the_form.the_text.value = the_time;
//每半秒钟执行一次该函数
the_timeout= setTimeout('writeTime();',500);
}
function fixTime(the_time) {
if (the_time <10) { the_time = "0" + the_time; } return the_time; }
我们仔细研究一下代码。
var today = new Date();
正如new Array() 可以生成一个新的数组,你可以可以
用new Date() 生成一个新的日期对象。生成对象之后,
你可以对其提出你的问题。你生成的新的日期对象的括号
中间没有任何参数, 但JavaScript会查询计算机的始终
并用其生成新的日期对象。现在我们的日期对象名为
"today" ,我们可以从中提取相应的信息。
var hours = today.getHours();
这条用于获得当前的小时值。它是军队格式的时间,即,
如果当前时间是下午两点,则它返回的值是14。getHours()
是Javascript的日期对象内置的方法调用。
var minutes = today.getMinutes(); var seconds = today.getSeconds();
这几行原理和getHours()类似。
minutes = fixTime(minutes);
getMinutes存在一些问题,如果分钟是11:01, getMinutes
将返回 "1"。时钟的显示格式可不是这样,它应该显示为
“01”。fixTime函数就是用于执行纠正显示格式的功能。
下面两行将字符串组合在一起并显示出来,
the_timeout = setTimeout('writeTime();', 500);
设置没半秒执行一次该函数的循环。
下面我们将学习如何在定时器中加入变量。
第六页:给定时器加入变量
定时器的功能主要在于设定某个未来的时间,让Javascript到
时执行某个语句:
var the_string = "hello";
the_timeout = setTimeout("alert(the_string);", 60000);
这两行指令让JavaScript等待一分钟然后弹出一个提示框显
示the_string变量所包含的内容。所以一分钟后JavaScript就
寻找叫做the_string的变量并调用alert()。但是问题是一分钟
后该变量中所包含的内容可能后是别的什么。所以如果你把这
两行代码放在一个函数内,setTimeout有可能会报错。例如在
下例中:
function alertInAMinute()
{
var the_string = "hello";
the_timeout = setTimeout("alert(the_string);", 60000);
}
然后你在某个链接中调用这个函数:
<a href="#" onClick="alertInAMinute(); return false;">blah!</a>
可能会出错。因为你定义名为the_string的变量时用的是var,
而当你在一个函数内使用var时,Javascript将其理解为该变量
只存在于该函数中。一旦离开该函数而进入另一个函数后,该
变量就会从Javascript的记忆中消失。所以当后来需要调用该变
量时,Javascript从它的记忆中是不可能找到该变量的,错误因
此就产生了。
产生问题的原因在于你将一个变量传递给setTimeout。你可以通
过将变量的值而不是变量本身传递给setTime来避免这个问题:
function alertInAMinute()
{
var the_string = "hello";
the_timeout = setTimeout("alert(" + the_string + ");",60000);
}
这段代码将变量the_string从setTimeout的引号中拉出来,由于
该变量变量目前不在setTimeout的引号内,JavaScript就可以从
记忆中找到该变量的值。
定时器在动态HTML中用途非常广泛,所以值得你仔细研究它。
要使所有的拥护都能看到动态HTML的内容,你必须了解如何识别
用户所使用的浏览器。
第七页:识别用户的浏览器
我所讲到的大部分内容都可以在支持JavaScript的浏览器中
运行。但是在某些浏览器上有些功能却不能正常执行。MSIE
3.0就不支持图片替换。
有几种办法可以处理 不同的浏览器。最简单的就是首先确定
用户所用的浏览器是什么类型,然后根据浏览器的类型执行相
应的指令。
有些网站直接让用户选择自己的浏览器类型然后执行相应的
指令。但这样并不是一个好办法。用户通常很反感这种让自己
自报家门的事情。同时针对用户所使用的浏览器的版本的不同
你必须做出不同版本的网页。
而用JavaScript就可以识别用户使用的是何种浏览器,然后自
动将用户导引到支持用户浏览器的网页。但这种方法仍须迫使
你为一个页面做出不同的若干个版本并随时进行更新。
更好的办法是让你的网页更聪明,让它针对不同的浏览器显示
不同的内容。
第八页:如何识别用户的浏览器
每种浏览器除了有Document(文件)对象之外还有
Navigator对象,Navigator有两个属性包含着你所使用的浏览
器类型的所有信息:appName and appVersion.
appName是浏览器的类型,如果你用的是Netscape的某种版本,
你的Javascript中可以加入这样一行:
var the_browser_name = navigator.appName;
则the_browser_name的值是 "Netscape"。对于 MSIE,
则the_browser_name是"Microsoft Internet Explorer" 。所以
如果你想知道某人用的何种浏览器就可以用appName。
appVersion是浏览器的版本.其显示信息例子:"4.03 [en]
(Win95; I)"。意思是Netscape version 4.03, English
International, for Win95/NT。将版本号从中提取出来的办法
很简单:只需抓取第1个数字即可。最快捷的办法就是使用函
数parseFloat(),它将字符串中第1个数字符号提取出来并返回
数值:
var the_browser_version = navigator.appVersion;
var the_version_number = parseFloat(the_browser_version);
除了版本号之外appVersion别的信息都不是很确切。不同的浏
览器可能会在该字符串中加入各种希奇古怪的东西。
现在我们利用以上获得的信息作一些有用的事。点击下面的按
钮你会看到Navigator最直观的用途。
以下是用到的两个函数。点按钮调用
browserSizeUp():
function browserSizeUp()
{
var browser = navigator.appName;
var version = versionNumber();
var the_string = browser + " " + version;
if ((browser == "Netscape" || browser == "Microsoft Internet Explorer") &&
(version >= 4))
{
alert("The browser doctor says: " + the_string + "? Now that's a beautiful browser!");
} else {
alert("The browser doctor says: " + the_string + ", Hmm. Maybe it's time to upgrade.");
}
}
function versionNumber() {
// return version number (e.g., 4.03)
return parseFloat(navigator.appVersion)
}
会发生什么呢?如果我们检测到的用户的浏览器版本大于4,则
显示一个提示框,否则就显示另外的提示框。在实际的应用中
你常常需要用到document.writeln() 生成适应不同浏览器的
HTML。
用户所看到的页面内容取决于它所使用的浏览器,浏览器的
版本。随着浏览器版本不断增多,又分别应用于不同的操作
平台,你要想浏览器的变化随时保持同步的困难不断加大。
所幸的是还有一种办法可以避免这个问题。
第九页:对象和方法的识别
最佳的方法是你先确定某一段代码具备什么功能,然后搞清楚
浏览器具备什么功能。
例如,我们知道有些浏览器支持图片替换,有些则不行。好在
可以检测浏览器是否具备这种功能:只需检测浏览器是否具
有document.images对象即可:
if (document.images)
{
do lots of image swap stuff
}
这样图片替换的代码就只会在支持图片替换的浏览器中才执行。
这种技巧还可以用于方法和函数。不是所有的;浏览器都有各
种方法。例如,在Netscape 4中,你可以用window.moveTo()方
法将窗口在屏幕中移动,如果你想在调用该方法之前先检测浏
览器是否具有这种方法,则:
if (window.moveTo)
{
make that window fly
}
确保浏览器支持某项功能后再调用该功能可以避免很多无谓的
脚本错误。使用浏览器功能测试的方法而不是浏览器类型测试
的方法可以使你的网页内容同各种平台上各种类型各种版本的
浏览器都保持同步。
第十页:History对象
在Javascript对象结构中还有两项我们没有涉及,即Packages
和History。要了解Packages需要先学习Java,但History则
是JavaScript特有的而且非常有用。
History包含着你的浏览器访问过的URL的记录列表.History对
象使你能利用window.history.back()和
window.history.forward()在列表各项记录中来回切换。back
的功能和你的浏览器上的back按钮的作用是一致的。
这两种方法在帧(分割窗口)中非常有用,因为在帧中浏览器
的back按钮常会把你带到你不想去的网页。