
1.5 使用脚本元素script标签
脚本存在的意义是可以在网页中放入一段动态代码,让网页能够动起来,这就是我们常说的动态网页,这些动态代码一般称为脚本语言代码。动态网页可以实现网页与用户的交互。
1.5.1 HTML网页内嵌脚本让网页动起来
HTML网页中的script标签用于定义脚本语言,主要包括JavaScript、VBScript、ECMAScript等脚本语言,目前最流行的当然是JavaScript脚本语言了,所以我们提到的嵌入脚本指的就是嵌入JavaScript脚本。
在前文1.1节中,我们向读者介绍了script标签的基本使用方法,这里先明确使用script标签嵌入脚本的语法,看下面几行:
<script type="text/javascript"> …… </script>
如果读者想在HTML网页中嵌入脚本,那么一般需要遵循上面的写法,并将脚本语言写在两个script标签之间。
下面我们看一段代码,让读者体会使用script标签嵌入脚本的方法。这段代码(参见源代码chapter01/ch01-htmlcomp-script-embed.html文件)是一个在HTML网页内不同位置嵌入脚本的应用,这个应用稍微有点复杂,后面我们会详细讲解。
【示例1-17】 HTML网页内嵌脚本
01 <!DOCTYPE html> 02 <html lang="zh"> 03 <head> 04 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 05 <style type="text/css"> 06 div { 07 margin: 2px; 08 padding: 2px; 09 width: auto; 10 height: auto; 11 border: 1px solid #000; 12 } 13 </style> 14 <script type="text/javascript"> 15 document.write("<h5>log: header - script</h5>"); 16 document.close(); 17 document.getElementById("id-div-header").innerHTML+="<br><h3>header- script</h3>"; 18 </script> 19 <title>HTML之嵌入式script</title> 20 </head> 21 <body> 22 <!-- 添加文档主体内容 --> 23 <h3>HTML之嵌入式script</h3> 24 <hr> 25 <!-- 添加文档主体内容 --> 26 <div id="id-div-header">header script log:</div> 27 <!-- 添加脚本 --> 28 <script type="text/javascript"> 29 document.write("<h5>log: dynamic - script</h5>"); 30 document.write("<div id='id-div-dynamic'>dynamic script log:</div>"); 31 document.close(); 32 document.getElementById("id-div-dynamic").innerHTML+="<h3>dynamic-script</h3>"; 33 </script> 34 <!-- 添加文档主体内容 --> 35 <div id="id-div-body">body script log:</div> 36 <!-- 添加脚本 --> 37 <script type="text/javascript"> 38 document.write("<h5>log: body - script</h5>"); 39 document.close(); 40 document.getElementById("id-div-body").innerHTML += "<br><h3>body - script</h3>"; 41 </script> 42 </body> 43 </html>
在这段HTML代码中使用的全部是内嵌脚本。下面解释这段代码。
先来看第14~18行,这里是一段内嵌在head标签中的脚本代码。其中,第15行使用document.write()方法尝试向HTML网页中写入一行日志,因为调用document.write()方法将会打开一个输出流,所以在第16行使用document.close()方法对其进行了关闭(建议读者予以重视);第17行使用document.getElementById()方法尝试在一个div标签(id值为"id-div-header",在第26行中定义)内插入一段文本。
然后来看第28~33行,这里是一段内嵌在body标签中的脚本代码。其中,第29~30行使用document.write()方法打开一个输出流,尝试向HTML网页中写入一行日志并动态创建了一个div标签(id值为"id-div-dynamic"),并在第31行使用document.close()方法关闭了该输出流;第32行使用document.getElementById()方法尝试在这个动态创建的div标签内插入一段文本。
最后来看第37~41行,这里同样是一段内嵌在body标签中的脚本代码。其中,第38行使用document.write()方法打开一个输出流,尝试向HTML网页中写入一行日志,并在第39行使用document.close()方法关闭了该输出流;第40行使用document.getElementById()方法尝试在一个div标签(id值为"id-div-body",在第35行中定义)内插入一段文本。
下面我们运行测试这个页面,效果如图1.22所示。

图1.22 HTML网页内嵌脚本效果图
从图1.22中看到页面中输出的信息比较多,下面我们对图中输出的结果进行详细的解释。
最先输出的是一行日志信息,通过查看【示例1-17】的代码知道是第15行中输出的。而第2行输出的信息是第23行定义的,可见在head标签之间定义的第14~18行脚本代码最先被编译执行了,在HTML网页DOM树被解析之前就执行了,这完全是基于在HTML网页定义的JavaScript脚本语言编译执行一项基本原则,即“按顺序载入,载入即执行”。而第17行脚本代码尝试在div标签(id值为"id-div-header")中插入文本信息的操作却没有显示。第3行输出的div标签(带细线边框风格)没有发生预期的改变,这是因为该标签是在第26行定义的,是在脚本操作之后,相当于操作时还没有被定义。
然后,页面第4行输出的也是一行日志信息,通过查看【示例1-17】的代码知道是第29行输出的。页面第5行显示的是第30行动态创建的div标签(id值为"id-div-dynamic",带细线边框风格),而第32行尝试在动态创建的div标签中插入文本的操作也成功显示了,见页面第6行的文本输出。
最后,页面中第7行显示的是第35行定义的div标签(id值为"id-div-body",带细线边框风格),而第40行尝试在该div标签中插入文本的操作也成功显示了,见页面中第8行的文本输出。页面第9行输出的也是一行日志信息,通过查看【示例1-17】的代码知道是第38行中输出的,虽然第38行定义在第40行之前,会先于第40行执行,但由于id值为"id-div-body"的div标签是在第35行中定义的,所以第38行定义的文本信息是最后被显示输出的。
通过上面的分析,读者会对HTML网页中定义的JavaScript脚本语言的编译执行顺序有一个初步了解。JavaScript脚本执行顺序需要遵循以下几个大的原则:
● 按顺序载入。
● 载入即执行。
● 执行时会阻塞后续内容。
所以,为了避免第17行没有被有效执行的情况出现,一般建议将自定义脚本放在body标签的最后(或HTML网页最后),这样可以保证全部HTML网页DOM树载入后再执行脚本。当然,JavaScript语言实际运用时非常复杂,上面的几个原则是最基本的,读者可以自行深入研究。
1.5.2 载入外部脚本库
这一小节我们介绍载入外部脚本库的方法,这里先明确使用script标签载入外部脚本库的语法:
<script type="text/javascript" src="url.js"></script>
如果读者想在HTML网页中载入外部脚本库,一般需要遵循上面的写法,并建议放在head标签内。但是根据1.5.1小节中所介绍的JavaScript脚本执行的原则,有时脚本引入需放在标签声明之后,才能被正常执行。
下面我们看一段载入外部脚本库的代码,这段代码(参见源代码chapter01/ch01-htmlcompscript-src.html文件)是一个在HTML网页内载入外部脚本库的应用。
【示例1-18】 HTML网页载入外部脚本
01 <!DOCTYPE html> 02 <html lang="zh"> 03 <head> 04 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 05 <style type="text/css"> 06 div { 07 margin: 2px; 08 padding: 2px; 09 width: auto; 10 height: auto; 11 border: 1px solid #000; 12 } 13 </style> 14 15 <title>HTML之载入外部脚本</title> 16 </head> 17 <body> 18 <!-- 添加文档主体内容 --> 19 <h3>HTML之载入外部脚本</h3> 20 <hr> 21 <!-- 添加文档主体内容 --> 22 <div id="id-div-body">body script log:</div> 23 <script type="text/javascript" src="js/src.js"></script> 24 </body> 25 </html>
在这段HTML代码中使用的是载入外部脚本的方法。下面解释这段代码。
第23行使用script标签载入了一个外部脚本,其中通过src属性定义了该脚本的路径为"js/src.js",该路径是一个基于本HTML网页的相对路径,将其转换成绝对路径为"chapter01/js/src.js"。
下面这段脚本代码(参见源代码chapter01/js/src.js文件)是在【示例1-18】的HTML代码中载入外部脚本库。
【示例1-19】 HTML网页载入外部脚本库
01 window.onload = function() { 02 document.getElementById("id-div-body").innerHTML += "<br><h3>body - script</h3>"; 03 }
在这段HTML代码中使用的全部是内嵌脚本。下面解释这段代码。
第02行使用document.getElementById()方法尝试在一个div标签(id值为"id-div-body",在【示例1-18】第22行中定义)内插入一段文本。
下面我们运行测试这个页面,页面打开后的效果如图1.23所示。第02行脚本代码定义的操作成功显示出来了,可见外部脚本与嵌入脚本的执行效果是完全一样的。

图1.23 HTML网页载入外部脚本效果图
1.5.3 推迟脚本执行
在前面的1.5.1小节中介绍过脚本语言定义在标签前面而导致操作失效的代码,这一小节我们给出推迟脚本执行的方法。通过在script标签中增加一个defer属性就可以实现推迟脚本执行的功能。使用defer属性的语法如下:
<script type="text/javascript" src="url.js" defer></script>
如果读者想在HTML网页中推迟脚本执行,一般需要遵循上面的写法,并与src属性结合使用。
下面我们来看一段推迟脚本执行的代码,这段代码(参见源代码chapter01/ch01-htmlcompscript-defer.html文件)是一个在HTML网页内测试推迟脚本执行的应用。
【示例1-20】 HTML网页推迟脚本执行
01 <!DOCTYPE html> 02 <html lang="zh"> 03 <head> 04 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 05 <style type="text/css"> 06 div { 07 margin: 2px; 08 padding: 2px; 09 width: auto; 10 height: auto; 11 border: 1px solid #000; 12 } 13 </style> 14 <title>HTML之载入外部脚本</title> 15 </head> 16 <body> 17 <!-- 添加文档主体内容 --> 18 <h3>HTML之载入外部脚本</h3> 19 <hr> 20 <!-- 添加脚本 --> 21 <script id="id-script-nodefer" type="text/javascript" src="js/nodefer.js"> 22 </script> 23 <!-- 添加文档主体内容 --> 24 <div id="id-div-nodefer">no defer script log:</div> 25 <!-- 添加脚本 --> 26 <script id="id-script-defer" type="text/javascript" src="js/defer.js" defer> 27 </script> 28 <!-- 添加文档主体内容 --> 29 <div id="id-div-defer">defer script log:</div> 30 <!-- 添加脚本 --> 31 <script type="text/javascript"> 32 document.getElementById("id-div-nodefer").innerHTML += 33 "<h3>defer value is " + 34 document.getElementById("id-script-nodefer").defer + 35 "</h3>"; 36 document.getElementById("id-div-defer").innerHTML += 37 "<h3>defer value is " + 38 document.getElementById("id-script-defer").defer + 39 "</h3>"; 40 </script> 41 </body> 42 </html>
在这段HTML代码中主要载入了两个外部脚本,其中一个是使用defer属性定义的,另一个则没有使用defer属性。下面我们分析这段代码。
第21行使用script标签载入了一个外部脚本,其中通过src属性定义了该脚本的路径为"js/nodefer.js",注意并没有使用defer属性。
第26行同样使用script标签载入了一个外部脚本,其中通过src属性定义了该脚本的路径为"js/defer.js",注意其添加了defer属性。
第31~40行主要获取了两个script标签的defer属性值,测试定义与不定义defer属性的script标签的取值。
下面这段脚本代码(参见源代码chapter01/js/nodefer.js文件)实现在【示例1-20】第21行代码中载入外部脚本库。
【示例1-21】 HTML网页推迟脚本执行脚本代码(1)
document.getElementById("id-div-nodefer").innerHTML += "<h3>no defer added.</h3>";
这段脚本代码尝试在div标签(id值为"id-div-nodefer",在【示例1-20】中第24行定义)中插入文本信息。
下面这段脚本代码(参见源代码chapter01/js/defer.js文件)实现在【示例1-20】第26行代码中载入外部脚本库,其主要代码如下:
【示例1-22】 HTML网页推迟脚本执行脚本代码(2)
document.getElementById("id-div-defer").innerHTML += "<h3>defer added.</h3>";
这段脚本代码尝试在div标签(id值为"id-div-defer",在【示例1-20】中第29行定义)中插入文本信息。
下面我们运行测试这个页面,效果如图1.24所示。【示例1-22】中脚本代码的操作成功显示出来了,而【示例1-21】中脚本代码的操作却没有显示出来,可见定义了defer属性的外部脚本是可以推迟执行的。同时,定义了defer属性的script标签取值为true,而没有定义defer属性的script标签取值为false,通过判断该值可以判定脚本在何时被执行。

图1.24 HTML网页推迟脚本执行效果图
1.5.4 异步执行脚本
在前面的1.5.1小节中介绍过脚本会以阻塞方式运行,有些时候这并不是我们想要的效果。这一小节我们给出异步执行脚本的方法,通过在script标签中增加一个async属性就可以实现异步执行脚本的功能。使用async属性的语法如下:
<script type="text/javascript" src="url.js" async></script>
如果读者想在HTML网页中异步执行脚本,一般需要遵循上面的写法,并与src属性结合使用。
下面我们看一段异步执行脚本的代码,这段代码(参见源代码chapter01/ch01-htmlcompscript-async.html文件)是一个在HTML网页内异步执行脚本的应用。
【示例1-23】 HTML网页异步执行脚本
01 <!DOCTYPE html> 02 <html lang="zh"> 03 <head> 04 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 05 <style type="text/css"> 06 /** h3 */ 07 h3 { 08 margin: 8px; /** 设置外边距 */ 09 padding: 4px; /** 设置内边距 */ 10 font: italic 18px/1.8em arial,verdana; /** 设置字体 */ 11 letter-spacing: 1.0em; /** 设置字符间距 */ 12 } 13 /** p */ 14 p { 15 margin: 4px; /** 设置外边距 */ 16 padding: 2px; /** 设置内边距 */ 17 font: bold 12px/1.2em arial,verdana; /** 设置字体 */ 18 letter-spacing: 2px; /** 设置字符间距 */ 19 } 20 </style> 21 <script type="text/javascript" src="js/noasync.js"></script> 22 <script type="text/javascript" src="js/async.js" async></script> 23 <title>HTML之异步执行脚本</title> 24 </head> 25 <body> 26 <!-- 添加文档主体内容 --> 27 <h3>HTML之异步执行脚本</h3> 28 <hr> 29 <!-- 添加文档主体内容 --> 30 <p>说明: async属性规定一旦脚本可用,则会异步执行.</p><br> 31 <p>备注: async属性仅适用于外部脚本(只有在使用 src 属性时).</p><br> 32 </body> 33 </html>
在这段HTML代码中主要载入了两个外部脚本,其中一个是使用async属性定义的,另一个则没有使用async属性。下面我们向读者分析这段代码。
第21行使用script标签载入了一个外部脚本,其中通过src属性定义了该脚本的路径为"js/noasync.js",注意并没有使用async属性。
第22行同样使用script标签载入了一个外部脚本,其中通过src属性定义了该脚本的路径为"js/async.js",注意其添加了async属性。
下面这段脚本代码(参见源代码chapter01/js/noasync.js文件)实现在【示例1-23】第21行代码中载入外部脚本库。
【示例1-24】 HTML网页异步执行脚本代码(1)
alert("no async js");
下面这段脚本代码(参见源代码chapter01/js/async.js文件)实现在【示例1-23】中第22行代码中载入外部脚本库。
【示例1-25】 HTML网页异步执行脚本代码(2)
alert("async js");
下面我们运行测试这个页面,页面打开后的效果如图1.25所示。

图1.25 HTML网页异步执行脚本效果图(1)
从图1.25中看到,【示例1-24】中脚本代码的警告消息框先被显示出来,而【示例1-23】中第25~32行定义的页面内容却没有显示出来,可见【示例1-23】中第21行定义的外部脚本是以阻塞方式执行的。
下面单击消息框中的“确定”按钮,接着运行这个页面,之后的效果如图1.26所示。

图1.26 HTML网页异步执行脚本效果图(2)
从图1.26中看到,【示例1-25】中脚本代码的警告消息框先被显示出来,同时【示例1-23】中第25~32行定义的页面内容也被显示出来,可见【示例1-23】中第22行定义的外部脚本是以异步方式执行的。
下面继续单击消息框中的“确定”按钮,接着运行这个页面,之后的效果如图1.27所示。

图1.27 HTML网页异步执行脚本效果图(3)
对比图1.26与图1.27可以看到,通过async属性定义脚本是以异步方式加载执行的,使用async属性可以大大丰富网页脚本的设计手段。
另外,async属性与defer属性具有一定的关联性,根据HTML 4官方文档的解释说明,二者存在以下关系:
● 如果定义async,那么脚本相对于页面的其余部分异步执行(当页面继续进行解析时,脚本将被执行)。
● 如果不定义async,但是定义defer,那么脚本将在页面完成解析时执行。
● 如果既不定义async又不定义defer,那么在浏览器继续解析页面之前立即以阻塞方式读取并执行脚本。
读者可以参考相关文档进行深入的学习。