前言
这是一本有意义的书
从 js 的起源到 js 的基础语法,再讲到 dom,并通过一个个详实的案例将 dom 编程的法则与技巧娓娓道来,如平稳退化、渐进增强、结构样式分离、性能优化。虽然许多编程片段随着技术的演进已经有了更好的方案,但书中描述的思想,对日常开发场景也有指导借鉴意义。比如
- 平稳退化,链接属性加 "#",防止 js 失效后功能完全失效
- 性能优化,合并文件减少网络请求,压缩脚本提高加载速度
- 结构与行为分离,html,css,js 分开协作
这些都能与框架开发一一对应,如骨架屏在某些情况下可以成为兜底的呈现内容;框架及对应插件为我们提供组件化懒加载能力,按需请求减少了并行请求数;同时打包工具还提供压缩插件减小生产环境包体积。
这是一本有故事的书
这是我和青训营的小伙伴交换阅读的。Shared joy is a double joy;换书读也是一样的道理。而触发这一事件的契机是尝试了《学习敏捷度》课程中提到的 “seeking”(求新)。这门课程的实践经验非常具有实用性,个人认为非常正确。(elearning.feishu.cn/student/cou…
这是一本勾起思绪的老书
对于日新月异的前端技术,层出不穷的前端工具链、框架,2011 年出版。彼时 Jquery 处于巅峰时期,随书的一些网站(见参考链接)可见业内为了标准化做的努力的历史,但是网站都好久没有更新了,会感受到一种历史的痕迹,我一想到就会有种莫名的难以言状感觉,(长江后浪推前浪、辞旧迎新、新旧交替等)说不清楚是不是伤感。
主体
主体分为两部分,关键名词解释以及重要思想论述。这些离我们日常开发都不远,大家稍微想一下就能回忆起来,就当复习一下基础知识。
对象的两种类型
-
内建对象(p30):
- 预先定义好、可以直接拿来用的对象,如 Date、Array。
-
宿主对象(p31):
- 预先定义好、可以直接拿来用的对象,但不是 js 语言本身提供的,而是由运行环境提供的,如网页上的 document 对象。
节点的三种类型以及 nodeType 的含义(p35)
-
nodeType (p54)
- 文档里几乎每一样东西都是节点,包括空格和换行。每一个节点都有 nodeType 属性,其属性值是数字。
-
元素节点
- 构成文档结构;
- ;元素可以包含其他元素, 没有被其他元素包含,是根元素。nodeType 属性值是 1。
-
属性节点
- 对元素做更具体的描述。title、id 等属性;并非所有的元素都包含属性,但所有的属性都被元素包含。nodeType 属性值是 2。
-
文本节点
- 构成文档内容;大多数内容由文本提供;XHTML 文档里,文本节点总是被包含在元素节点的内部。nodeType 属性值是 3。
DOM 0、DOM 1 以及事件添加函数
主要考虑平台通用性、出现时间、书写简便性。
-
“第 0 级 DOM”(p3):
-
Javascript 的早期版本向程序员提供了查询和操纵 Web 文档某些实际内容(主要是图像和表单)的手段,因为 Javascript 预先定义了 “images”、“forms” 等术语,我们才能像下面这样在 Javascript 脚本里引用 “文档中的第三个图像” 或 “文档中名为 'details' 的表单”:
document.images[2]document.forms['details']
-
人们通常把这种试验阶段的初级 DOM 称为 “第 0 级 DOM”(DOM Level 0);这种方法访问属性只适用于部分属性,并且只适用于 Web 文档。
-
-
“第 1 级 DOM”(p49)
-
setAttribute 方法是 “第 1 级 DOM”(DOM Level 1)的组成部分,它可以设置任意元素节点的任意属性。在 “第 1 级 DOM” 出现之前,可以通过另一种办法设置大部分元素的属性,例如,想要改变某个 input 元素的的 value 属性,可以这样:
element.value = "the new value"- 这与下面这条语句的效果是等价的:
element.setAttribute("value","the new value")
-
DOM 是一种适用于多种环境和多种程序设计语言的通用型 API,也可以用在 Web 浏览器以外的环境里
-
- 事件添加函数(p83)
这个函数的作用某些情况下就如 addEventListener 一样,通过这个函数能够在 window.onload 上挂载各种函数,规避了
window.onload=func1;window.onload=func2;这种会导致 onload 被覆盖的写法
function addLoadEvent(func) { var oldonload = window.onload; if (typeof window.onload != 'function') { window.onload = func; } else { window.onload = function() { oldonload(); func(); } } }- setAttribute 表里不一(p45)
DOM Core 和 HTML-DOM(p93)的异同
以下方法属于 DOM Core,它们不专属于 js,支持 DOM 的任何一种程序设计语言都可以使用它们:
getElementById
getElementByTagName
getAttribute
setAttribute
以下方法属于 HTML-DOM,它们只能用来处理 Web 文档。
element.src
document.forms
除此之外,HTML-DOM 在 DOM Core 出现之前很久就已经为人们所熟悉了,同样的操作用两种方法都可以,HTML-DOM 写起来会简洁一点。
document.getElementByTagName("form") document.forms element.getAttribute("src") element.src element.setAttribute("src","source") element.src="source"结构层、行为层、表现层的含义(p150、p201)
HTML5 是 HTML 语言当前及未来的新标准。HTML 规范从 HTML4 到 XHTML,再到 Web Apps 1.0,
最后又回到 HTML5,整个成长历程充满了艰辛和争议。HTML5 问世背后的明争暗斗活像一部肥皂剧(这部戏中的一些情节至个还在延续),不管怎样,结局还是圆满的。我们有理由为 HTML5 欢呼,因为多种技术统一的趋势日益明朗,它标志着下一代 Web 的帷幕正在缓缓拉开。
谈到Web设计,最准确的理解是把网页看成三个层:
(1)结构层
(2)样式层
(3)行为层
这三个层分别对应不同的技术,分别是:
(1)超文本标记语言(HTML)
(2)层叠样式表(CSS)
(3)JavaScript 和文档对象模型(DOM)
没错,你可以说还能再加一层,也就是浏览器的 JavaScript API,包括 cookie 和 window 等。 但随着HTML5 的到来,上面所说的结构层、样式层和行为层(以及浏览器中的JavaScript API)已经被整装到一个小集合中:新的标记元素、更多媒体元素;新元素的交互方式、高级选择器、渐变、动画;Socket、Storage、多线程。
渐进增强思想(p67、p122)
考虑平稳退化的例子,在 HTML 文档里使用诸如 onclick 之类的属性是一种既没有效率,也容易引发问题的做法。考虑把事件处理函数从元素节点中分离,网页就会健壮得多。
所以这里通过 class 属性等挂钩获取元素,循环为元素绑定事件。
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>Example</title> <script type="text/JavaScript"> window.onload = function() { var lnks = document.getElementsByTagName("a"); for (var i=0; i<lnks.length; i++) { if (lnks[i].getAttribute("class") == "popup") { lnks[i].onclick = function() { popUp(this.getAttribute("href")); return false; } } } } function popUp(winURL) { window.open(winURL,"popup","width=320,height=480"); } </script> </head> <body> <a href="http://www.example.com/" class="popup">Example</a> </body> </html>-
js 切换 className(p167)
- 直接用 DOM 修改样式,这种让 “行为层” 干 “表示层” 的活,并不是理想的工作方式。如果你改变了主意,想换换那些由 DOM 脚本设置的样式,就不得不埋头于 Javascript 函数中去寻找和修改与样式设置有关的语句。如果可以在样式表里进行那些修改,那就好多了。
- 这里有一种简明的解决方案:与其使用 DOM 直接改变某个元素的样式,不如通过 Javascript 代码去更新这个元素的 class 属性。
平稳退化思想(p63、p76、p122)
平稳退化是渐进增强的水到渠成的过程。
Javascript 未生效的情况也是需要考虑的,网站的访问者可能禁用 js,或者其他情况下 js 未能按照预期工作。这时候如果我们正确使用了 js 脚本,就可以让访问者在他们的浏览器不支持 js 的情况下仍能顺利浏览网站。所谓的平稳退化(graceful degradation)就是虽然某些功能无法使用,但最基本的操作仍能顺利完成。但是实现方式有优劣。
考虑实现点击超链接打开一个新的浏览器窗口的功能,有一个函数封装可被调用
function popUp(winURL) { window.open(winURL,"popup","width=320,height=480"); }- 使用 "javascript:" 伪协议
通过伪协议调用 popUp() 函数
<a href="javascript:popUp('http://www.example.com/');">Example</a>- 使用内联的事件处理函数
<a href="#" onclick="popUp('http://www.example.com/');return false;">Example</a>- 平稳退化
<a href="http://www.example.com/" onclick="popUp('http://www.example.com/');return false;">Example</a>前两种方法在 js 不能正常工作的情况下无法提供预期功能,平稳退化使得即使 js 被禁用,原本功能也没有完全失效。同时,这样对搜索机器人(searchbot)更友好,少数搜索机器人能理解 js 代码,如果做不到平稳退化,那么在搜索引擎上的排名就可能大受损害。
向后兼容思想(p70)
核心思想是根据是否存在来决定采取的行动,防止某些脚本不能正常工作。书中举例使用对象检测(object detection)
考虑渐进增强中的例子,多加了一行检测 getElementsByTagName 是否存在,若不存在则直接返回。
window.onload = function() { if (!document.getElementsByTagName) return false; var lnks = document.getElementsByTagName("a"); for (var i=0; i<lnks.length; i++) { if (lnks[i].getAttribute("class") == "popup") { lnks[i].onclick = function() { popUp(this.getAttribute("href")); return false; } } } }本质上是为了防止不存在、不支持导致的错误,这个除了靠我们的 if else 检测技术实现,日常开发过程中还有许多类似的思想,如
- tsx 组件中根据某一值判断,若存在则渲染、三元表达式选择展示组件还是兜底组件
{isButtonGroupVisible && ( <ButtonGroup></ButtonGroup> )}- 可选链,读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效
obj?.para- lodash 的 get 方法获取属性,不存在则返回假值或我们可以设置兜底值
var object = { 'a': [{ 'b': { 'c': 3 } }] }; _.get(object, 'a.b.c', 'default'); // => 'default'思考
参考链接
作者探讨 js 网站(很多年没更新了):domscripting.com/
作者个人网站(经常更新):adactio.com/
随书下载:www.ituring.com.cn/book/42
Web 标准计划小组(很多年没更新了):www.webstandards.org/
- 构成文档结构;