2.1 使用<script>标签
通常情况下,在Web页面中使用JavaScript有两种方法,一种是在页面中直接嵌入JavaScript代码,另一种是链接外部JavaScript文件。
在HTML页面中嵌入JavaScript脚本需要使用<script>标签,用户可以在<script>标签中直接编写JavaScript代码,或者单独编写JavaScript文件,然后通过<script>标签导入。
2.1.1 编写第一个JavaScript程序
下面通过示例演示如何使用<script>标签的两种方式(直接在页面中嵌入JavaScript代码和链接外部JavaScript文件)。
【示例1】直接在页面中嵌入JavaScript代码。
第1步,新建HTML文档,保存为test.html。然后在<head>标签内插入一个<script>标签。
第2步,为<script>标签指定type属性值为"text/javascript"。现在的浏览器默认<script>标签的类型为JavaScript脚本,因此省略type属性,依然能够被正确执行,但是考虑到代码的兼容性,建议定义该属性。
第3步,直接在<script>标签内部输入JavaScript代码:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>test</title> <script type="text/javascript"> function hi(){ document.write("<h1>Hello, World! </h1>"); } hi(); </script> </head> <body> </body> </html>
上面JavaScript脚本先定义了一个hi()函数,该函数被调用后会在页面中显示字符"Hello, World! "。document表示DOM网页文档对象,document.write()表示调用Document对象的write()方法,在当前网页源代码中写入HTML字符串"<h1>Hello, World! </h1>"。
调用hi()函数,浏览器将在页面中显示一级标题字符"Hello, World! "。
第4步,保存网页文档,在浏览器中预览,显示效果如图2-1所示。
图2-1 第一个JavaScript程序
提示:包含在<script>标签内的JavaScript代码被浏览器从上至下依次解释。
当使用<script>标签嵌入JavaScript代码时,不要在代码中的任何地方输出"</script>"字符串。例如,浏览器在加载下面所示的代码时就会产生一个错误:
<script type="text/javascript"> function hi(){ document.write("</script>"); } hi(); </script>
错误原因:当浏览器解析到字符串"</script>"时,会结束JavaScript代码段的执行。
解决方法:
<script type="text/javascript"> function hi(){ document.write("<\/script>"); } hi(); </script>
使用转义字符把字符串"</script>"分成两部分来写就不会造成浏览器的误解。
【示例2】链接外部JavaScript文件。
第1步,新建文本文件,保存为test.js。注意,扩展名为.js,它表示该文本文件是JavaScript类型的文件。
提示:使用<script>标签包含外部JavaScript文件时,默认文件类型为JavaScript,因此.js扩展名不是必需的,浏览器不会检查包含JavaScript的文件的扩展名。在高级开发中,使用JSP、PHP或其他服务器端语言动态生成JavaScript代码时可以使用任意扩展名,如果不使用.js扩展名,用户应确保服务器能返回正确的MIME类型。
第2步,打开test.js文本文件,在其中编写下面代码,定义简单的输出函数。
function hi(){ alert("Hello, World! "); }
在上面代码中,alert()表示Window对象的方法,调用该方法将弹出一个提示对话框,显示参数字符串"Hello, World! "。
第3步,保存JavaScript文件,注意与网页文件的位置关系。这里保存JavaScript文件位置与调用该文件的网页文件位于相同目录下。
第4步,新建HTML文档,保存为test1.html。然后在<head>标签内插入一个<script>标签。定义src属性,设置属性值为指向外部JavaScript文件的URL字符串。代码如下:
<script type="text/javascript" src="test.js"></script>
第5步,在上面<script>标签下一行继续插入一个<script>标签,直接在<script>标签内部输入JavaScript代码,调用外部JavaScript文件中的hi()函数。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>test</title> <script type="text/javascript" src="test.js"></script> <script type="text/javascript"> hi(); //调用外部JavaScript文件的函数 </script> </head> <body> </body> </html>
第6步,保存网页文档,在浏览器中预览,显示效果如图2-2所示。
图2-2 调用外部函数弹出提示对话框
提示:定义src属性的<script>标签不应再包含JavaScript代码。如果嵌入了代码,则只会下载并执行外部JavaScript文件,嵌入代码会被忽略。
<script>标签的src属性可以包含来自外部域的JavaScript文件。例如:
<script type="text/javascript" src="http://www.sothersite.com/test.js"></script>
这些位于外部域中的代码也会被加载和解析。因此在访问自己不能控制的服务器上的JavaScript文件时要小心,防止恶意代码,或者防止恶意人员随时可能替换JavaScript文件中的代码。
【拓展】HTML为<script>定义了6个属性,简单说明如下。
async:可选。表示应该立即下载脚本,但不应妨碍页面中其他操作,如下载其他资源或等待加载其他脚本。该功能只对外部JavaScript文件有效。
charset:可选。表示通过src属性指定的代码的字符集。由于大多数浏览器会忽略它的值,因此很少使用。
defer:可选。表示脚本可以延迟到文档完全被解析和显示之后再执行。该属性只对外部JavaScript文件有效。IE 7及更早版本对嵌入的JavaScript代码也支持这个属性。
language:已废弃。原来用于表示编写代码使用的脚本语言,如JavaScript、JavaScript l.2或VBScript。大多数浏览器会忽略这个属性,不建议再使用。
src:可选。表示包含要执行代码的外部文件。
type:可选。可以看成是language的替代属性,表示编写代码使用的脚本语言的内容类型(也称为MIME类型)。虽然text/javascript和text/ecmascript已经不被推荐使用,但人们一直习惯使用text/javascript。服务器在传送JavaScript文件时使用的MIME类型通常是application/x-javascript,但在type中设置这个值可能导致脚本被忽略。另外,在非IE浏览器中还可以使用application/javascript和application/ecmascript。考虑到约定俗成和浏览器最大限度的兼容性,目前在客户端,type属性值一般使用text/javascript。不过,这个属性并不是必需的,如果没有指定这个属性,则其默认值仍为text/javascript。
2.1.2 脚本位置
所有<script>标签都会按照它们在HTML中出现的先后顺序依次被解析。在不使用defer和async属性的情况下,只有在解析完前面<script>标签中的代码之后,才会开始解析后面<script>标签中的代码。
【示例1】在默认情况下,所有<script>标签都应该放在页面头部的<head>标签中。
<!doctype html> <html> <head> <meta charset="utf-8"> <title>test</title> <script type="text/javascript" src="test.js"></script> <script type="text/javascript"> hi(); </script> </head> <body> <!-- 网页内容 --> </body> </html>
这样就可以把所有外部文件(包括CSS文件和JavaScript文件)的引用都放在相同的地方。但是,在文档的<head>标签中包含所有JavaScript文件,意味着必须等到全部JavaScript代码都被下载、解析和执行完成以后,才能开始呈现页面的内容。如果页面需要很多JavaScript代码,这样无疑会导致浏览器在呈现页面时出现明显的延迟,而延迟期间的浏览器窗口中将是一片空白。
【示例2】为了避免延迟问题,现代Web应用程序一般都把全部JavaScript引用放在<body>标签中页面的内容后面。
<!doctype html> <html> <head> <meta charset="utf-8"> </head> <body> <!-- 网页内容 --> <<title>test</title> <script type="text/javascript" src="test.js"></script> <script type="text/javascript"> hi(); </script> /body> </html> </html>
这样,在解析包含的JavaScript代码之前,页面的内容将完全呈现在浏览器中,同时会感到打开页面的速度加快了。
2.1.3 延迟执行脚本
为了避免脚本在执行时影响页面的构造,HTML为<script>标签定义了defer属性。defer属性能够迫使脚本被延迟到整个页面都解析完毕后再运行。因此,在<script>标签中设置defer属性,相当于告诉浏览器虽然可以立即下载JavaScript代码,但延迟执行。
【示例】在下面的示例中,虽然把<script>标签放在文档的<head>标签中,但其中包含的脚本将延迟到浏览器遇到</html>标签后再执行。
<!doctype html> <html> <head> <script type="text/javascript" defer src="test1.js"></script> <script type="text/javascript" defer src="test2.js"></script> </head> <body> <!-- 网页内容 --> </body> </html>
HTML5规范要求脚本按照它们出现的先后顺序执行,因此第一个延迟脚本会先于第二个延迟脚本执行,而这两个脚本会先于DOMContentLoaded事件执行。在实际应用中,延迟脚本并不一定会按照顺序执行,也不一定会在DOMContentLoaded事件触发前执行,因此最好只包含一个延迟脚本。
提示:defer属性只适用于外部脚本文件。这一点在HTML5中已经明确规定,因此支持HTML5的实现会忽略给嵌入脚本设置的defer属性。IE 4~IE 7还支持对嵌入脚本的defer属性,但IE 8及之后版本则完全支持HTML5规定的行为。
IE 4、Firefox 3.5、Safari 5和Chrome是最早支持defer属性的浏览器。其他浏览器会忽略这个属性。因此,把延迟脚本放在页面底部仍然是最佳选择。
注意:在XHTML类型的文档中,defer属性应该定义为defer="defer"。
2.1.4 异步响应脚本
HTML5为<script>标签定义了async属性。这个属性与defer属性类似,都用于改变外部脚本的行为。同样与defer类似,async只适用于外部脚本文件,并告诉浏览器立即下载文件。但与defer不同的是,标记为async的脚本并不保证按照指定它们的先后顺序执行。
【示例】在下面的代码中,第二个脚本文件test2.js可能会在第一个脚本文件test1.js之前执行。因此,用户要确保两个文件之间没有逻辑顺序的关联,互不依赖是非常重要的。
<!doctype html> <html> <head> <script type="text/javascript" async src="test1.js"></script> <script type="text/javascript" async src="test2.js"></script> </head> <body> <!-- 网页内容 --> </body> </html>
指定async属性的目的是不让页面等待两个脚本文件下载完后再执行,从而异步加载页面其他内容。
提示:异步响应的脚本一定会在页面的load事件前执行,但可能会在DOMContentLoaded事件触发之前或之后执行。异步脚本不要在加载期间修改DOM。
支持异步脚本的浏览器包括Firefox 3.6+、Safari 5和Chrome。在XHTML文档中,要把async属性设为async="async"。
2.1.5 在XHTML中使用JavaScript脚本
XHTML(EXtensible HyperText Markup Language)表示可扩展超文本标记语言,是将HTML作为XML的应用而重新定义的一个标准。编写XHTML代码的规则要比编写HTML严格得多,而且直接影响能否在嵌入JavaScript代码时使用<script/>标签。
提示:将页面的MIME类型指定为application/xhtml+xml.的情况下会触发XHTML模式,当然并不是所有浏览器都支持这种方法。
【示例】下面的代码块虽然在HTML中是有效的,但在XHTML中则是无效的。
<script type="text/javascript"> function test(a, b){ if(a < b){ alert(a + "<" + b); } else if(a > b){ alert(a + ">" + b); } else{ alert(a + "=" + b); } } test(1,3); </script>
在HTML中,有特殊的规则用以确定<script>标签中的哪些内容可以被解析,但这些特殊的规则在XHTML中不适用。例如,上面代码中比较语句a<b中的小于号(<)在XHTML中将被当作开始一个新标签来解析。但是作为标签来讲,小于号后面不能跟空格,因此就会导致语法错误。
避免在XHTML中出现类似语法错误的方法如下。
方法一,使用相应的HTML转码(&1t;)替换代码中所有的小于号(<),替换后的代码如下:
<script type="text/javascript"> function test(a, b){ if(a < b){ alert(a + "<" + b); } else if(a > b){ alert(a + ">" + b); } else{ alert(a + "=" + b); } } </script>
虽然这样可以让代码在XHTML中正常运行,但却导致代码不好理解了。
方法二,使用一个CData片段来包含JavaScript代码。在XHTML中,CData片段是文档中的一个特殊区域,这个区域中可以包含不需要解析的任意格式的文本内容。因此,在CData片段中就可以使用任意字符,且不会导致语法错误。引入CData片段后的JavaScript代码块如下:
<script type="text/javascript"><![CDATA[ function test(a, b){ if(a < b){ alert(a + "<" + b); } else if(a > b){ alert(a + ">" + b); } else{ alert(a + "=" + b); } } ]]></script>
在兼容XHTML的浏览器中,这个方法可以解决问题。但实际上,还有不少浏览器不兼容XHTML,因而不支持CData片段。这时可以使用JavaScript注释将CData标记注释掉。
<script type="text/javascript"> //<![CDATA[ function test(a, b){ if(a < b){ alert(a + "<" + b); } else if(a > b){ alert(a + ">" + b); } else{ alert(a + "=" + b); } } test(1,3); //]]> </script>
这样在所有现代浏览器中都可以正常使用,它能通过XHTML验证,而且能够兼容XHTML之前的浏览器。
2.1.6 兼容不支持JavaScript的浏览器
最早引入<script>标签时,与传统HTML的解析规则存在冲突。由于要对这个标签应用特殊的解析规则,因此在那些不支持JavaScript的浏览器中就会导致问题,即会把<script>标签的内容直接输出到页面中,因而会破坏页面的布局和外观。
Netscape与Mosaic协商并提出了一个解决方案,让不支持<script>标签的浏览器能够隐藏嵌入的JavaScript代码。这个方案就是把JavaScript代码包含在一个HTML注释中。
【示例】下面的代码给脚本加上HTML注释后,不支持JavaScript的浏览器就会忽略<script>标签中的内容,而那些支持JavaScript的浏览器在遇到这种情况时,则必须进一步确认其中是否包含需要解析的JavaScript代码。
<script><!- function test(a, b){ alert("ok"); } //--></script>
虽然这种注释JavaScript代码的格式得到了所有浏览器的认可,也能被正确解释,但由于所有浏览器都已经支持JavaScript,因此也就没有必要再使用这种格式了,也不再推荐使用这种方法。
在XHTML模式下,因为脚本包含在XML注释中,所以脚本会被忽略。