整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

带你走进JavaScript世界系列-事件处理程序之HTML事件处理

面我们已经介绍了事件的概念,而响应某个事件的函数就叫做事件处理程序。事件处理程序的名字以 "on" 开头,因此 click 事件的处理程序就是 onclick ,load 事件的处理程序就是 onload。

HTML 事件处理程序

某个元素支持的每种事件,都可以使用一个相应事件处理程序同名的 HTML 特性来指定。这个特性的值应该是能够执行的 JavaScript 代码。例如,要在按钮被单击时执行一些操作,可以像下面代码一样:

<input type="button" value="点击我" onclick="alert('我被点击了。。')" /> 

注意,上面这种写法不能在其中使用未经转义的 HTML 语法字符,如果要使用 HTML 语法字符,就需要使用转义字符了。

在 HTML 中定义的事件处理程序可以包含要执行的具体动作,也可以调用在页面其他地方定义的脚本,例:

HTML 事件处理程序--调用页面脚本

上面代码指定事件处理程序具有一些独到之处。首先,会创建一个封装着元素属性值的函数。这个函数中有一个局部变量 event,通过这个 event 变量,可以直接访问事件对象,你不用自己定义它,也不用从函数的参数列表中读取。在这个函数内部,this 值等于事件的目标元素。

不过,在 HTML 中指定事件处理程序有两个缺点。首先,存在一个时差问题,用户可能在 HTML 元素一出现在页面上就触发了相应的事件,但当时的事件处理程序有可能尚不具备执行条件。前面的例子中,假如 showMessage() 函数是在按钮下方、页面的最底部定义的。如果用户在页面解析 showMessage() 函数之前就单击了按钮,就会出现错误。因此,很多 HTML 事件处理程序都会被封装在一个 try-catch 块中。其次,这样扩展事件处理程序的作用域链在不同浏览器中会导致不同结果。不同 JavaScript 引擎遵循的标识符解析规则存在差异,很可能会在访问非限定对象成员时出错。

者 | 单雨

责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

模板继承

简介

模板继承允许你建立一个基本的"骨架"模板, 它包含了网站中所有常见的元素,并定义了可以被子模板覆盖的 块(blocks) 。示例:

假如父模板base.html如下:

<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
<div id="sidebar">
{% block sidebar %}
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
{% endblock %}
</div>

<div id="content">
{% block content %}{% endblock %}
</div>
</body>
</html>

它定义了一个简单的 HTML 骨架文档, 假设这是一个简单的两列页面。子模板的工作就是填充空的 块(block) 中的内容。

在这个例子中, block 标签定义了三个可以被子模板填充的块。block标签告诉了模板系统哪些地方可能被子模板覆盖。例如,子模板可能如下:

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
<h2>{{ entry.title }}</h2>
<p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

extends 标签告诉模板系统这个模板继承了另外的模板。当模板系统对此模板进行运算时, 首先会寻找他的父模板 ——在这里是"base.html"。

在这一点上, 模板引擎会在 base.html 中发现三个 block 标签, 并且使用子模板的内容替换掉这些块。根据变量blog_entries 的值, 输出可能看起来像这样:

<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<title>My amazing blog</title>
</head>

<body>
<div id="sidebar">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</div>

<div id="content">
<h2>Entry one</h2>
<p>This is my first entry.</p>

<h2>Entry two</h2>
<p>This is my second entry.</p>
</div>
</body>
</html>

注意:因为子模板没有定义 sidebar 块, 那么父模板的内容就会被使用。通常来说, 父模板 {% block %} 中的内容会被作为备用的内容,在子模板没有覆盖时就会被使用。

多重继承

模板继承可以是多重继承,多重继承常见的模式是:

  • 创建一个 base.html 模板把控网站的整体风格。

  • 为网站的每个子分类创建一个 baseSECTIONNAME.html模板. 比如, basenews.html, base_sports.html。 这些模板都继承 base.html 模板。这些模板中包含特定的设计/风格。

  • 为每一种类型的页面创建一个模板, 比如 news article 或blog 内容。这些模板扩展上一级模板的相应分类。

上述的关系可以用下图表示:

这样就能最大限度的重用模板代码,比如在所有页面通用的导航栏。

模板继承注意事项

  • {% extends %}必须位于模板的最开始, 如果在其他的部分声明, 则不生效。

  • 在基础模板尽可能多的使用{%block%} ,子模板不需要定义所有父模板中的块, 所以你可以在若干的块中填充默认值, 然后定义之后需要自定义的块, 有更多的可用块总是更好的。

  • 如果发现自己在许多模板中有重复内容的了, 这可能需要移动这些内容到父模板的 {% block %} 中。

  • 如果需要得到父模板块中的内容, 可以用{{ block.super }} 变量。使用 {{ block.super }} 插入的数据不会被自动转义 ,因为它已经被转义了。如果需要转义, 可以在父模板中转义。

  • 使用模板标签在{% block %}块外部创建的变量不能在块内使用。例如,这个模板不渲染任何东西:

 {% trans "Title" as title %}
{% block content %}{{ title }}{% endblock %}
  • 为了具有更好的可读性,也可以给{% endblock %}块标签定义一个名字。例如:

 {% block content %}
...
{% endblock content %}

在一个较长的模板中, 这个方法可以让你知道是哪一个{% block %} 标签定义结束了。

  • 不能在同一模板中定义多个具有相同名称的块标签。存在这个限制是因为{%block%}标签是"双向"定义的。也就是说, 它不仅指定了子模板要填充父模板的哪个块, 也说明了父模板要引用哪些子模板块的内容。所以在子模板中有多个同名的{%block%}标签时, 父模板就不知道到底要引用子模板中哪个块的内容了。

自动HTML转义

从模板直接生成HTML存在XSS风险

从模板生成HTML时, 总是有一种风险, 即一个变量将会影响生成的HTML字符。考虑这个模板片段:

Hello, {{ name }}

看起来可能没有什么风险,但是如果用户输入的名字为一个HTML代码:

<script>alert('hello')</script>

当{{name}}为这个值时,模板将呈现为:

Hello, <script>alert('hello')</script>

这将使浏览器弹出一个弹出一个JavaScript警告。同样的,考虑另外一种情况:

如果名字中包含一个 '<' 符号:

<b>username

对应的模板将为:

Hello, <b>username

这意味着在此之后的文字将呈现为粗体。

由此带来一个风险:

用户提交的数据是不可靠的且不应被直接插入到您的网页, 因为恶意用户可以使用这种潜在的漏洞做危害网站的事情。这种类型的安全漏洞被称为 Cross Site Scripting (跨站脚本) (XSS) 攻击。

使用转义避免XSS风险

(1)使用escape标签

确保让不受信任的变量经过了 escape 过滤器 , 将危险的HTML字符替换为无害的HTML转义字符。但是这常常被忽略。

(2)使用自动转义

在Django中, 默认每个模板会自动转义输出的每一个变量标签。具体来说, 这五个字符会被转义:

  • < 被替换为 &lt;

  • > 被替换为 &gt;

  • ' (单引号) 被替换为 &#39;

  • " (双引号) 被替换为 &quot;

  • & 被替换为 &amp;

这种行为是默认的。如果使用的是Django的模板系统, 自然拥有这种保护。

关闭自动转义

可以在站点,模板和变量三个层级关闭自动转义。

(1)对单个变量

要为一个单独的变量禁用自动转义, 使用 safe 过滤器(https://docs.djangoproject.com/zh-hans/2.2/ref/templates/builtins/#std:templatefilter-safe):

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

如果变量中包含“\”字符,输出也是“\”。

(2)对于模板文本块

要在模板中控制自动转义, 可以在整个模板 (或者模板的特定区域) 使用 autoescape 标签, 如:

{% autoescape off %}
Hello {{ name }}
{% endautoescape %}}}

autoescape 标签接受 on 或 off 作为参数。如果需要在某个区域禁用自动转义,可以这样使用:

{% autoescape off %}
This will not be auto-escaped: {{ data }}.

Nor this: {{ other_data }}
{% autoescape on %}
Auto-escaping applies again: {{ name }}
{% endautoescape %}
{% endautoescape %}

(3)自动转义的继承特性

如果使用自动转义的模板文本块中使用{%block%}包含了子模板,那么子模板中也将使用自动转义,如果基模板中关闭了自动转义,那么子模板中也将关闭自动转义:

base.html:

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}

child.html:

{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

因为基模板中关闭了自动转义,子模板中也将关闭自动转义,所以在HTML渲染时,greeting 变量被输出为 <b>Hello!</b>:

<h1>This &amp; that</h1>
<b>Hello!</b>

(4)注意事项

模板的开发者并不需要对自动转义太过关注. 这是编写Python的开发者 (写视图和写自定义模板标签的人) 需要考虑的事. 所以, 你只需要做和模板有关的活。如果你不确定模板在何时会进行自动转义(或不进行), 那么就向所有需要转义的变量添加 escape 过滤器. 当自动转义被打开, escape 过滤器不会再次转义 -- escape 过滤器不会影响自动转义的变量。

(5)字符串和自动转义

过滤器的参数可以是字符串,例如:

{{ data|default:"This is a string literal." }}

作为过滤器参数的字符串都不会自动转义, django认为它们已经经过safe 过滤。这么做的原因是模板的开发者可以控制字符的输出, 所以他们应该确保正确的使用转义后的HTML字面值。

这意味着你应该这么写:

{{ data|default:"3 &lt; 2" }}

而不是:

{{ data|default:"3 < 2" }} 

模板中的对象方法调用

大多数附加到对象上的方法都可以在模板中调用,这使得模板可以从视图中传递过来的上下文变量中获取对象属性之外的值。

例如, Django ORM 提供了 "entry_set" 语法寻找一个与外键所关联的对象的集合。因此, 如果有一个叫 "comment" 的模型有外键指向了模型 "task",你可以通过给定一个实际的 task模板变量, 像这样循环输出与它相关联所有的 comment对象:

{% for comment in task.comment_set.all %}
{{ comment }}
{% endfor %}

如果你在一个模型中显示定义了一个方法,你也可以在对应的模板变量中引用它:

模型

class Task(models.Model):
def foo(self):
return"bar"

模板调用

{{ task.foo }}

注意:由于Django有意限制了模板中语言逻辑的处理,所以不能在模板内调用对象方法时向其传递参数。数据应当在视图中计算完成后,再传递给模板。

作者简介:单雨,90后工科男,伪文艺青年。目前就读于北京理工大学宇航系,喜欢研究AI,网络爬虫,微信小程序以及机器人,痴迷于Coding,睡前必撸码。

【END】

习目标:了解JavaScript是如何与HTML结合来创建动态网页,网页中嵌入JavaScript的不同方式,JavaScript的内容类型及其与<script>的关系

<script>元素

<script>是由Netscape创造出来,后来加到HTML规范中的。

<script>有8个属性:

1、async:表示立即开始下载脚本,但不能阻止其他页面动作,比如下载资源或者等待其他脚本加载。只对外部脚本文件有效。

2、charset:使用src属性指定代码字符集。这个属性很少用,因为大多数浏览器不在乎它的值。

3、crossorigin;配置资源请求的CORS(跨源资源共享)设置。默认情况下不使用CORS。crossorigin = “anonymous”配置文件请求不用设置凭据标志。crossorigin = ”use-credentials“设置凭据标志,意味着出站请求会包含凭据。

4、defer:表示脚本可以延迟到文档全部解析和显示后再执行。新版本中只能用于外部脚本。

5、integrity:允许比对接收到的资源和指定的加密签名以验证子资源完整性(SRI,Subresource integrity),如果验证签名不匹配则脚本不会执行。这个属性可以用于确保内容分发网络(CDN,Content Delivery Network)不会提供恶意内容。

6、language:此属性已被废止。

7、src:表示包含外部要执行的代码的外部文件。

8、type:代替language,表示代码块中脚本语言的内容类型(也称为MIME类型),按照惯例这个值始终都是”text/JavaScript“,尽管”text/JavaScript“和”text/ecmascript“都已经废弃。JavaScript文件的MIME类型通常是”application/x-javascript“,不过给type属性这个值的话可能会导致脚本被忽略。在非IE的浏览器中有效的值还有”application/JavaScript“和”application/ecmascript"。如果这个值是module,则代码会被当成是ES6模块,而且只有这时候代码中才能出现import和export关键字。

使用<script>的方式有内联和外嵌两种,只要把code写入<script>code</script>中就好,code中要是包含字符串“<script>”,只要加上转义字符“\”即可。

如果要外嵌JavaScript代码只要使用src属性来链接外部文件即可如:

<script src=“example.js”></script>

XHTML 文档中,可以忽略结束标签写成<script src=“example.js”/>即可,但是这在HTML中不能使用。

标签位置

过去把JavaScript和CSS一起写在head中,但是这意味着必须下载所有code并解析和解释完成后才开始渲染页面,对于JavaScript很多的页面会导致页面渲染速度过慢,为解决这个问题,JavaScript一般写在body元素的页面内容的最后边,如下

<html>

<head></head>

<body>

message

<script>code<\script>

<\body>

</html>

推迟使用脚本

在外联JavaScript时可以使用defer属性来推迟脚本的运行。可以写成:

<html>

<head>

<script defer src = "example.js">code<\script>

</head>

<body>

message

<\body>

</html>

异步执行脚本

async属性从脚本处理方式上与defer类似,但是不同的是标记async的脚本并不能保证脚本按照他们的出现顺序执行,比如:

<html>

<head>

<script sync src = "example1.js">code<\script>

<script sync src = "example2.js">code<\script>

</head>

<body>

message

<\body>

</html>

不能保证example1比example2先执行。

动态加载脚本

除了<script>以外还可以用其他方式加载脚本。因为JavaScript可以使用DOM API,所以通过向DOM中动态地加入script元素同样可以加载指定脚本。只要创建一个script元素并将其添加到DOM即可。

let script = document.createElement('script');

script.src = 'gibberish.js';

document.head.appendChild(script);

当然,在把 HTMLElement 元素添加到 DOM 且执行到这段代码之前不会发送请求。默认情况下,以这种方式创建的<script>元素是以异步方式加载的,相当于添加了 async 属性。不过这样做可能会有问题,因为所有浏览器都支持 createElement()方法,但不是所有浏览器都支持 async 属性。因此,如果要统一动态脚本的加载行为,可以明确将其设置为同步加载:

let script = document.createElement('script');

script.src = 'gibberish.js';

script.async = false;

document.head.appendChild(script);

以这种方式获取的资源对浏览器预加载器是不可见的。这会严重影响它们在资源获取队列中的优先级。根据应用程序的工作方式以及怎么使用,这种方式可能会严重影响性能。要想让预加载器知道这些动态请求文件的存在,可以在文档头部显式声明它们:

<link rel="preload" href="gibberish.js">

XHTML中的变化

可扩展超文本标记语言(XHTML,Extensible HyperText Markup Language)是将 HTML 作为 XML的应用重新包装的结果。与 HTML 不同,在 XHTML 中使用 JavaScript 必须指定 type 属性且值为text/javascript,HTML 中则可以没有这个属性。XHTML 虽然已经退出历史舞台,但实践中偶尔可能也会遇到遗留代码,为此本节稍作介绍。在 XHTML 中编写代码的规则比 HTML 中严格,这会影响使用<script>元素嵌入 JavaScript 代码。下面的代码块虽然在 HTML 中有效,但在 XHML 中是无效的。

<script type="text/javascript">

function compare(a, b) {

if (a < b) {

console.log("A is less than B");

} else if (a > b) {

console.log("A is greater than B");

} else {

console.log("A is equal to B");

}

}

</script>

在 HTML 中,解析<script>元素会应用特殊规则。XHTML 中则没有这些规则。这意味着 a < b语句中的小于号(<)会被解释成一个标签的开始,并且由于作为标签开始的小于号后面不能有空格,这会导致语法错误。避免 XHTML 中这种语法错误的方法有两种。第一种是把所有小于号(<)都替换成对应的 HTML实体形式(<)。结果代码就是这样的:

<script type="text/javascript">

function compare(a, b) {

if (a < b) {

console.log("A is less than B");

} else if (a > b) {

console.log("A is greater than B");

} else {

console.log("A is equal to B");

}

}

</script>

这样代码就可以在 XHTML 页面中运行了。不过,缺点是会影响阅读。好在还有另一种方法。第二种方法是把所有代码都包含到一个 CDATA 块中。在 XHTML(及 XML)中,CDATA 块表示文档中可以包含任意文本的区块,其内容不作为标签来解析,因此可以在其中包含任意字符,包括小于号,并且不会引发语法错误。使用 CDATA 的格式如下:

<script type="text/javascript"><![CDATA[

function compare(a, b) {

if (a < b) {

console.log("A is less than B");

} else if (a > b) {

console.log("A is greater than B");

} else {

console.log("A is equal to B");

}

}

]]></script>

在兼容 XHTML 的浏览器中,这样能解决问题。但在不支持 CDATA 块的非 XHTML 兼容浏览器中则不行。为此,CDATA 标记必须使用 JavaScript 注释来抵消:

<script type="text/javascript">

//<![CDATA[

function compare(a, b) {

if (a < b) {

console.log("A is less than B");

} else if (a > b) {

console.log("A is greater than B");

} else {

console.log("A is equal to B");

}

}

//]]>

</script>

这种格式适用于所有现代浏览器。虽然有点黑科技的味道,但它可以通过 XHTML 验证,而且对XHTML 之前的浏览器也能优雅地降级。

废弃的语法

自 1995 年 Netscape 2 发布以来,所有浏览器都将 JavaScript 作为默认的编程语言。type 属性使用一个 MIME 类型字符串来标识<script>的内容,但 MIME 类型并没有跨浏览器标准化。即使浏览器默认使用 JavaScript,在某些情况下某个无效或无法识别的 MIME 类型也可能导致浏览器跳过(不执行)相关代码。因此,除非你使用 XHTML 或<script>标签要求或包含非 JavaScript 代码,最佳做法是不指定 type 属性。在最初采用 script 元素时,它标志着开始走向与传统 HTML 解析不同的流程。对这个元素需要应用特殊的解析规则,而这在不支持 JavaScript 的浏览器(特别是 Mosaic)中会导致问题。不支持的浏览器会把<script>元素的内容输出到页面上,从而破坏页面的外观。Netscape 联合 Mosaic 拿出了一个解决方案,对不支持 JavaScript 的浏览器隐藏嵌入的 JavaScript 代码。最终方案是把脚本代码包含在一个 HTML 注释中,像这样:

<script><!--

function sayHi(){

console.log("Hi!");

}

//--></script>

使用这种格式,Mosaic 等浏览器就可以忽略<script>标签中的内容,而支持 JavaScript 的浏览器则必须识别这种模式,将其中的内容作为 JavaScript 来解析。虽然这种格式仍然可以被所有浏览器识别和解析,但已经不再必要,而且不应该再使用了。在XHTML 模式下,这种格式也会导致脚本被忽略,因为代码处于有效的 XML 注释当中。

行内代码与外部文件

虽然可以直接在 HTML 文件中嵌入 JavaScript 代码,但通常认为最佳实践是尽可能将 JavaScript 代码放在外部文件中。不过这个最佳实践并不是明确的强制性规则。推荐使用外部文件的理由如下。

 可维护性。JavaScript 代码如果分散到很多 HTML 页面,会导致维护困难。而用一个目录保存所有 JavaScript 文件,则更容易维护,这样开发者就可以独立于使用它们的 HTML 页面来编辑代码。

 缓存。浏览器会根据特定的设置缓存所有外部链接的 JavaScript 文件,这意味着如果两个页面都用到同一个文件,则该文件只需下载一次。这最终意味着页面加载更快。

 适应未来。通过把 JavaScript 放到外部文件中,就不必考虑用 XHTML 或前面提到的注释黑科技。包含外部 JavaScript 文件的语法在 HTML 和 XHTML 中是一样的。在配置浏览器请求外部文件时,要重点考虑的一点是它们会占用多少带宽。在 SPDY/HTTP2 中,预请求的消耗已显著降低,以轻量、独立 JavaScript 组件形式向客户端送达脚本更具优势。比如,第一个页面包含如下脚本:

<script src="mainA.js"></script>

<script src="component1.js"></script>

<script src="component2.js"></script>

<script src="component3.js"></script>

...

后续页面可能包含如下脚本:

<script src="mainB.js"></script>

<script src="component3.js"></script>

<script src="component4.js"></script>

<script src="component5.js"></script>

...

在初次请求时,如果浏览器支持 SPDY/HTTP2,就可以从同一个地方取得一批文件,并将它们逐个放到浏览器缓存中。从浏览器角度看,通过 SPDY/HTTP2 获取所有这些独立的资源与获取一个大JavaScript 文件的延迟差不多。在第二个页面请求时,由于你已经把应用程序切割成了轻量可缓存的文件,第二个页面也依赖的某些组件此时已经存在于浏览器缓存中了。当然,这里假设浏览器支持 SPDY/HTTP2,只有比较新的浏览器才满足。如果你还想支持那些比较老的浏览器,可能还是用一个大文件更合适。

文档模式

IE5.5 发明了文档模式的概念,即可以使用 doctype 切换文档模式。最初的文档模式有两种:混杂模式(quirks mode)和标准模式(standards mode)。前者让 IE 像 IE5 一样(支持一些非标准的特性),后者让 IE 具有兼容标准的行为。虽然这两种模式的主要区别只体现在通过 CSS 渲染的内容方面,但对JavaScript 也有一些关联影响,或称为副作用。本书会经常提到这些副作用。

IE 初次支持文档模式切换以后,其他浏览器也跟着实现了。随着浏览器的普遍实现,又出现了第三种文档模式:准标准模式(almost standards mode)。这种模式下的浏览器支持很多标准的特性,但是没有标准规定得那么严格。主要区别在于如何对待图片元素周围的空白(在表格中使用图片时最明显)。

混杂模式在所有浏览器中都以省略文档开头的 doctype 声明作为开关。这种约定并不合理,因为混杂模式在不同浏览器中的差异非常大,不使用黑科技基本上就没有浏览器一致性可言。标准模式通过下列几种文档类型声明开启:

<!-- HTML 4.01 Strict -->

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"

"http://www.w3.org/TR/html4/strict.dtd">

<!-- XHTML 1.0 Strict -->

<!DOCTYPE html PUBLIC

"-//W3C//DTD XHTML 1.0 Strict//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<!-- HTML5 -->

<!DOCTYPE html>

准标准模式通过过渡性文档类型(Transitional)和框架集文档类型(Frameset)来触发:

<!-- HTML 4.01 Transitional -->

<!DOCTYPE HTML PUBLIC

"-//W3C//DTD HTML 4.01 Transitional//EN"

"http://www.w3.org/TR/html4/loose.dtd">

<!-- HTML 4.01 Frameset -->

<!DOCTYPE HTML PUBLIC

"-//W3C//DTD HTML 4.01 Frameset//EN"

"http://www.w3.org/TR/html4/frameset.dtd">

<!-- XHTML 1.0 Transitional -->

<!DOCTYPE html PUBLIC

"-//W3C//DTD XHTML 1.0 Transitional//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<!-- XHTML 1.0 Frameset -->

<!DOCTYPE html PUBLIC

"-//W3C//DTD XHTML 1.0 Frameset//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

准标准模式与标准模式非常接近,很少需要区分。人们在说到“标准模式”时,可能指其中任何一个。而对文档模式的检测(本书后面会讨论)也不会区分它们。本书后面所说的标准模式,指的就是除混杂模式以外的模式。

<noscript>元素

针对早期浏览器不支持 JavaScript 的问题,需要一个页面优雅降级的处理方案。最终,<noscript>元素出现,被用于给不支持 JavaScript 的浏览器提供替代内容。虽然如今的浏览器已经 100%支持JavaScript,但对于禁用 JavaScript 的浏览器来说,这个元素仍然有它的用处。<noscript>元素可以包含任何可以出现在<body>中的 HTML 元素,<script>除外。在下列两种情况下,浏览器将显示包含在<noscript>中的内容:

 浏览器不支持脚本;

 浏览器对脚本的支持被关闭。任何一个条件被满足,包含在<noscript>中的内容就会被渲染。否则,浏览器不会渲染<noscript>中的内容。

下面是一个例子:

<!DOCTYPE html>

<html>

<head>

<title>Example HTML Page</title>

<script defer="defer" src="example1.js"></script>

<script defer="defer" src="example2.js"></script>

</head>

<body>

<noscript>

<p>This page requires a JavaScript-enabled browser.</p>

</noscript>

</body>

</html>

这个例子是在脚本不可用时让浏览器显示一段话。如果浏览器支持脚本,则用户永远不会看到它。

小结

JavaScript 是通过<script>元素插入到 HTML 页面中的。这个元素可用于把 JavaScript 代码嵌入到HTML 页面中,跟其他标记混合在一起,也可用于引入保存在外部文件中的 JavaScript。本章的重点可以总结如下。

 要包含外部 JavaScript 文件,必须将 src 属性设置为要包含文件的 URL。文件可以跟网页在同一台服务器上,也可以位于完全不同的域。

 所有<script>元素会依照它们在网页中出现的次序被解释。在不使用 defer 和 async 属性的情况下,包含在<script>元素中的代码必须严格按次序解释。

 对不推迟执行的脚本,浏览器必须解释完位于<script>元素中的代码,然后才能继续渲染页面的剩余部分。为此,通常应该把<script>元素放到页面末尾,介于主内容之后及</body>标签之前。

 可以使用 defer 属性把脚本推迟到文档渲染完毕后再执行。推迟的脚本原则上按照它们被列出的次序执行。

 可以使用 async 属性表示脚本不需要等待其他脚本,同时也不阻塞文档渲染,即异步加载。异步脚本不能保证按照它们在页面中出现的次序执行。

 通过使用<noscript>元素,可以指定在浏览器不支持脚本时显示的内容。如果浏览器支持并启用脚本,则<noscript>元素中的任何内容都不会被渲染。