第三课
第一页 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); }
试一下这个错误的定时代码看看会发生什么?
faulty timer code?
请注意当你等了三秒钟,三个定时信息之一神秘地出现在文本框里,然后就停在那儿。在上面的不好的代码中,每个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的浏览器中运行。但是在某些浏览器上有些功能却不能正常执行。MSIE3.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按钮常会把你带到你不想去的网页。