谈谈 《 JavaScript - DOM编程艺术 》这本书

news2024/12/27 13:07:19

前言

好吧,现在已经2023年了,对于这本书(第二版)来说可能有点老了,这本书不是很难理解,但也不是很适合新手读,当然,这本书并不是百宝书 📕 ,它更注重于编程的规范与思路、用户的体验、实战演练,并且我在这本书中学到了不少东西,我决定将这些知识分享出来

所以我接下来将分享以下话题的相关点:

  • 规范
  • 平稳退化(向后兼容)
  • 渐进增强
  • 着重于用户的体验
  • 模块化思想
  • 根据需求对原生DOM对象的二次封装

规范

对于编程规范来说,这本书真的让人受益匪浅,在进入DOM学习之前,我对初入DOM的新手同志说一段话:JavaScript和DOM并没有强制的关系,而是使用JavaScript去操作DOM,意思就是DOM对象是浏览器运行时提供的,你可以使用其他浏览器支持的语言去操作DOM对象,同时JavaScript在其他运行时中是不能去操作DOM对象的(因为根本没有DOM对象)比如Node.js,所以这个界限在初入学习DOM时是我们必须要知道的

结构层、样式层、行为层尽量分离

这个规范观点很有趣,也是我在这本书中学到的,之前我只是如同行尸走肉一般的去这样书写,不明白为什么,原来这也是一种规范,同时避免了不必要的报错,我们尽量的去将结构层、样式层、行为层进行分离:
接下来我举一个例子:

// 比如我在HTML超文本语言中去使用行内式JavaScript
<a href="javascript:;">点我</a>

// 我们可能经常去使用伪协议操作a元素,为了让他禁止默认跳转行为

javascript: '伪协议会让我们通过一个链接去调用JavaScript函数,然后里面什么也没有书写,也就是执行了一段空的JavaScript代码并且通过执行JavaScript而拦截了链接默认跳转行为

通过javascript: '伪协议从而调用JavaScript代码的做法是不好的,我们应该尽量做到结构层与行为层的分离

当然还有内嵌式的事件处理函数,我相信大家也不会去这样写,因为不仅没有做到规范,我们程序员自己的也很费劲

<a href="#" onclick="alert('这样做是不好的行为')">点我</a>

有时候这本书令我很’不适’

为什么这样说呢?因为它真的规范的不得了,这本书比较老,所以它对与IE的兼容以及不开启JavaScript功能的用户还是比较关注的
只要有假设的思想,那么它一定会进行一个对象检测,就像这样:

function test() {
	if (!document.getElementsByTagName) return false
	const links = document.getElementsByTagName("a")
	if (links.length !== 0) {
		// 执行相对应语句
	}
}

test()

所以坚持看完之后,我感觉整个人都升华了,太规范了

平稳退化

平稳退化在本书讲述的时候大多数是基于兼容IE来讲的,因为IE不支持的对象属性有点太多了,如果你的用户刚好用的就是IE,那么你必须要做好万全的准备,也就是所说的平稳退化,如果秉承了平稳退化的原则,那么哪怕在任何浏览器中打开你的网页都不会报错

平稳退化不仅仅适用于JavaScript操作DOM对象来说,它也适用于结构层的显示,我的意思是平稳退化是一种思想,而不是具体的相对的概念,有些内容是不必要的,我们在HTML架构中只是去书写一些必要的信息,那些通过JavaScript创建的元素(不必要信息)如果用户浏览器不支持DOM或者用户压根就关闭了JavaScript功能,它们就不能够呈现,这些内容必须是充分不必要条件,如果不支持,那么不显示的同时也不会报错或者影响用户阅读信息

还有不得不说的一点就是对象检测技术,上面的话题我们提到过了,就是在执行代码之前进行一个 if 判断,判断是否存在该元素、对象或者属性,如果存在该元素、对象或者属性,那么继续执行,如果不存在该元素、对象或者属性,那么 return false 跳出执行

在不支持该对象的情况下,我们退而求其次的呈现出不是那么好的效果,或者说不显示次要内容的情况下不会让网页报错导致不可控的情况

渐进增强

平稳退化、渐进增强这俩点是这本书的绝招

渐进增强基本上说的是围绕一个基本中心点来展开的拓展

也就是说我们需要编写一个所有浏览器都支持的基础版本,这个基础版本可能没有那么的好,但是基本的重要内容都以尽量最好的形式展现出来,在这个基础上我们去编写一些炫酷的、更有趣的并且会增加体验的内容,这些内容会在高版本浏览器中呈现,低版本浏览器则只会显示基本内容,这些不支持的属性,我们会使用上面提到的平稳退化来进行一个兼容与拦截报错

用户体验

我想这是最重要的环节,因为我们的网页最终都是要呈现给用户的,所以我们要以用户体验为中心,在渐进增强的基础上使用平稳退化技术,那么这就是一个较为完美的网页了
这并不容易,我们的用户可能在浏览网页时受到很多的阻碍

  • 比如用户的视力不是很好,那么我们在编写HTML属性(比如title、alt…)以及使用DOM创建一些节点元素的时候,就要格外的注意信息的呈现,当然这对网络爬虫也是非常友好的,这很利于SEO,关于SEO的优化,本书没有提到很多
  • 或者浏览器版本很低,甚至那时候有的浏览器连DOM都不支持,不过现在应该没有这种情况了,如果一款浏览器在现在还不支持DOM,那么它连出头之日都没有
  • 再或者用户关闭了JavaScript功能,那么一些内容就不能呈现在网页中了,这就是我们为什么不把重要内容使用DOM创建出来
  • …还有很多的情况,我们需要在每一个开发场景之下,尽量的去预见一些可能会发生的情况,并且在这之前采取一些必要的措施,当然一个程序不可能是绝对完美的,我们也不可能去预见所有的未知错误,我们需要不断的迭代与尽量做好预见措施

模块化思想的强化

这本书讲到的编程思想至少对我来说是受益匪浅的,其中模块化编程更是贯穿了整本书,作者为了我们便于阅读,将每一个功能存放于不同的JavaScript文件中,当然我说的不是这个模块化,我只是顺带提一嘴该书作者的良苦用心,这种一个功能一个JavaScript文件的做法是不提倡的,也是非常不利于用户的,因为100个功能存放在100个JavaScript文件中意味着用户要发起至少100次请求,这很滑稽

作者将每一个功能都封装在了一个函数中,然后在必要的时候去调用该方法,至少在本书中,我没有见到过作者声明全局变量(除了一个个封装完美、等待被调用的函数或者方法),至少这对于我们后期的维护非常友好,并且可以进行复用,避免命名冲突引起的不必要错误,等等…

基于原生DOM对象的二次封装

本书作者为我们提供了四个最基本的封装案例(甚至更多我没有细心发现),至少我觉得这够了,当我们去实现一个功能的时候没有对应的方法可以使用,我们为什么不去基于可用的方法去封装一个全局共享功能函数?

  • 封装全局共享 onload 事件
function addLoadEvent(func) {
	let oldOnload = window.onload
	if (typeof window.onload !== "function") {
		window.onload = func
	} else {
		oldOnload()
		func()
	}
}
  • 基于insertBefore封装一个insertAfter函数用来添加节点到指定节点之后
// 添加节点元素到某一个指定节点元素之后
function insertAfter(newElement, targetElement) {
	let parent = targetElement.parentNode
	if (parent.lastChild === targetElement) {
		parent.appendChild(newElement)
	} else {
		parent.insertBefore(newElement, targetElement.nextSibling)
	}
}
  • 基于className方法封装一个addClass函数用来添加类名
function addClass(ele, val) {
	if (!ele.className) ele.className = val
	ele.className = `${ele.className} ${val}`
}

好吧,我想大家现在都会去使用 element.classList.add() ,这个方法我们可能不会去使用,但是这个思想是值得我们去学习的

  • 动画函数的封装
// 请给到动画元素绝对定位属性以及父元素的一个相对定位属性
function moveAnimation(ele, finalX, finalY, interval) {
	// 关闭上一次定时器
	if (ele.movement) {
		clearTimeout(ele.movement)
	}
	// 预见措施
	if (!ele.style.left) {
		ele.style.left = "0px"
	}
	if (!ele.style.top) {
		ele.style.top = "0px"
	}
	let xpos = parseInt(ele.style.left)
	let ypos = parseInt(ele.style.top)
	let dist
	if (xpos === finalX && ypos === finalY) {
		return true
	}
	if (xpos < finalX) {
		dist = Math.ceil((finalX - xpos) / 10)
		xpos += dist
	}
	if (xpos > finalX) {
		dist = Math.ceil((xpos - finalX) / 10)
		xpos -= dist
	}
	if (ypos < finalY) {
		dist = Math.ceil((finalY - ypos) / 10)
		ypos += dist
	}
	if (ypos > finalY) {
		dist = Math.ceil((ypos - finalY) / 10)
		ypos -= dist
	}
	ele.style.left = `${xpos}px`
	ele.style.top = `${ypos}px`
	let repeat = [ele, finalX, finalY, interval]
	ele.movement = setTimeout(() => {
		moveAnimation(...repeat)
	}, interval)
}

结束补充

这本书对于浏览器历史战争与W3C的出现,ECMAScript规范的出现等等都有所提及,顺着历史线一直到今天,一直发展到这个地步真的是非常令人激动,对于前几章 – 作者照顾新手为JavaScript语言进行了一个简短的知识快速概括

如果在当年,这本书应该也是叱诧风云的杰作,对于现在来说,的确有些老了,廉颇老矣-常能饭否,我一直在强调这本书值得我们去学习的是它的一个思想

可能浏览器嗅探技术我们不会再使用到,但是对于对象检测技术我们还是需要去学习这个思想,对于什么地方可能会出现错误,什么地方可能获取不到元素或者数据?

在第六章第四节作者提到了:不要做太多假设,我对此的理解是不要做太多没有必要的假设,这会造成一个代码冗余,我们应该以全局来观细节

在之前我会将document.bodydocument.querySelector('body')这俩个属性混谈为DOM对象,这样称呼好像也未尝不可,不过准确来说,document.body是DOM-HTML的属性,而document.querySelector('body')是DOM Core的属性(比如我们还有document.src , onclcik),onclcik是DOM-HTML的属性,那我们使用DOM Core该怎么去触发事件处理函数?

– 没错,当然是去使用方法监听了addEventListener(),在本书也有提及到attachEvent(),不过这是一个非标准的方法监听函数,前些年还在用来兼容IE

并且作者在书中提到:函数在行为方面应该是一个自给自足的脚本,我很认同

对于节点树这个概念,大家可以自行搜索查阅相关概念,当你拥有节点树这个概念之后,我相信解决DOM问题会更加的得心应手(这也是我们必须要知道的)

最后提一个点,作者在书中不止一次提到了结构化程序设计的一条原则:函数应该只有一个入口和出口,同时作者又不止一次违反了这条原则,所以在实际工作中,我们应该去根据具体情况来选择具体的处理方案,做一些取舍,有时候失也可以是得

对于其他一些必要知识的补充

说到必要知识补充,我第一个就要推荐DOM事件流事件对象-event,在我们处理DOM时这些是必须要知道的

DOM事件流

事件流描述的是从页面中接收事件的顺序,事件发生时会在元素节点之间按照特定的顺序传播

DOM事件流分为三个阶段:

  • 捕获阶段
  • 当前目标阶段
  • 冒泡阶段

假如我们在body元素中创建了一个div元素,并且为它绑定了一个事件,那么它的事件流就是这样的:

事件流.png

JavaScript只能执行捕获或冒泡其中的一个阶段

DOM-HTML的onclick属性和attachEvent()方法只能得到冒泡阶段,而addEventListener()方法可以设置执行哪个阶段

addEventListener(type,listener[,useCapture])

第三个参数如果为true,那么会在事件捕获阶段调用事件处理程序 – 默认false在事件冒泡阶段调用事件处理程序

事件对象(event)

当我们创建了一个事件时,事件对象会被自动创建出来,事件对象是我们事件一系列相关数据的集合,它和事件相关,如果我们创建了一个onclick事件,那么这个事件的事件对象就是与点击事件相关的一系列信息

element.addEventListener('click',(event)=>{})

比如我们想要阻止链接的默认跳转行为,我们可以使用事件对象中的preventDefault()方法:

a.addEventListener('click',(event)=>{
  event.preventDefault()
})

// tips:低版本浏览器使用:event.returnValue
// 或者万能阻止行为:return false

// 上述提到的事件冒泡我们也可以使用stopPropagation进行拦截

事件委托

当一组元素需要注册事件的时候,我们不一定要一个一个的去注册事件(我不是说手动一个一个创建,我是说我们使用循环遍历为每一个元素绑定事件),这样性能不太高

我们为什么不去利用事件冒泡来处理这个场景问题呢?我们只把一个事件绑定到这些元素的父节点上,这样的话只操作一次DOM不就可以了吗?

实践出真知:

const ul = document.quselector('.testUL')
ul.addEventListener('click',(e)=>{
  e.target.style.backgroundColor = 'green'
})

我觉得这很酷!当然这和事件对象离不开,事件对象很好玩

对于BOM的补充

这本书注重于DOM,所以对于浏览器对象模型BOM的阐述并不是很多

我总结了几个比较常用的:

  1. location对象

…属性

  • location.href :获取当前网页URL地址
  • location.host :返回主机域名
  • location.port :返回端口号
  • location.pathname :返回路径
  • location.search :返回参数
  • location.hash :返回哈希值(片段)

…方法

  • location.assgin() :重定向页面(使用href属性也可以达到相同的效果)
  • location.replace() :替换当前页并且不记录历史(不能后退页面)
  • location.reload(true) :重载页面
  1. navigator对象
  • navigstor.userAgent.match(用户打开网页所使用的设备名称1 | 用户打开网页所使用的设备名称2…)

这不禁让我想到了浏览器嗅探

  1. history对象
  • back() :后退页面
  • forward() :前进页面
  • go(number/-number) :后退前进都可以

结束语

还是开头的那句话,这本书使我受益匪浅,也许它比较老,也许它不够全,但它很有趣,这就够了

这本书作者的结束语很棒:接下来路在何方,这个问题只能你自己来回答(翻译)

我们不能止步于此,这并不是DOM的全部,无论是当下还是未来,至少我们应该再去看看offset(元素偏移量)、client(元素相对视口信息)、scroll(滚动事件的专属事件对象)、touch(移动端触摸事件)、本地存储…

好吧,我压根没有打算说完这些,这个工作估计需要很长时间…



声明:我并没有任何好处去推荐该书,这只是一个读者对于这本书的一个观后感

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/351456.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

格式化字符串你都懂了吗

文章目录前言一、什么是格式化字符串&#xff1f;二、使用 % 格式化字符串三、使用 format() 格式化字符串总结前言 今天跟大家聊聊字符串的格式化这部分内容。乍一听“格式化”这三个字&#xff0c;有的初学者可能会懵&#xff1a;难道这是要清空字符串的节奏&#xff1f; 其…

网络管理之设备上线技术的发展现状和趋势

网络和网络设备无处不在 随着社会的发展和技术的进步&#xff0c;人类文明开始向信息时代演进&#xff0c;网络逐渐变成现代社会不可或缺的一部分&#xff0c;极大程度影响了人类的认知形式、思维方式与生活模式。从家庭网&#xff0c;到企业网&#xff1b;从无线网&#xff0…

Mysql MHA搭建

. 目录 机器配置 安装Docker Docker安装和启动Mysql8.0.26 搭建Mysql一主二从 设置三台机器免密访问 安装MHA 搭建问题记录 问题1 MHA验证主从复制报错Access denied; you need (at least one of) the SUPER, REPLICATION CLIENT privilege(s) for this operation 问题2 MHA验证…

doris - 数仓 拉链表 按天全量打宽表性能优化

数仓 拉链表 按天全量打宽性能优化现状描述优化现状描述 1、业务历史数据可以变更 2、拉链表按天打宽 3、拉链表模型分区字段设计不合理&#xff0c;通用的过滤字段没有作为分区分桶字段 4、拉链表表数据量略大、模型数据分区不合理和服务器资源限制&#xff0c;计算任务执行超…

安装JupyterLab失败的解决方案

由于本人电脑安装的Python版本3.6比较低&#xff0c;所以可能存在下面两种方法都安装失败&#xff0c;最后给出一个简单省事的方法。Jupyter lab比Jupyter Notebook要好用&#xff0c;试了之后感觉跟VSCode一样的存在&#xff0c;所以还是值得安装来代替Jupyter Notebook使用。…

Ae:解释素材

所谓解释素材 Interpret Footage&#xff0c;就是通过修改素材的某些属性&#xff08;像素长宽比、帧速率、颜色配置文件及 Alpha 通道类型等&#xff09;&#xff0c;让它能更好地参与到合成中去。Ae菜单&#xff1a;文件/解释素材快捷键&#xff1a;Ctrl Alt G在项目面板里…

【C语言】编程初学者入门训练(13)

文章目录121. 小乐乐算最高分122. 小乐乐计算求和123. 小乐乐计算函数124. 小乐乐查找数字125. kiki学程序设计基础126. kiki算期末成绩127. kiki说祝福语128. kiki的最高分129. 求质数的个数130. kiki去重整数并排序121. 小乐乐算最高分 问题描述&#xff1a;小乐乐的老师BoB…

4.2 双点双向路由重发布

1. 实验目的 熟悉双点双向路由重发布的应用场景掌握双点双向路由重发布的配置方法2. 实验拓扑 双点双向路由重发布如图4-6所示: 图4-6:双点双向路由重发布 3. 实验步骤 IP地址的配置R1的配置 <Huawei>system-v…

关键路径、工期、总时差和自由时差精讲

关键路径法是在进度模型中&#xff0c;估算项目最短工期&#xff0c;确定逻辑网络路径进度灵活性大小的一种方法。①计算原理a.计算ES、EF&#xff1a;从网络计划起点节点开始&#xff0c;沿箭线方向依次向前推算&#xff0c;数值取大。b.计算LS、LF&#xff1a;从网络计划终点…

SVFormer:走进半监督动作识别的视觉 Transformer

出品人&#xff1a;Towhee 技术团队 顾梦佳 半监督学习&#xff08;SSL&#xff09;的动作识别是一个关键的视频理解任务&#xff0c;然而视频标注的高成本加大了该任务的难度。目前相关的方法主要研究了卷积神经网络&#xff0c;较少对于视觉 Transformers&#xff08;ViT&…

一个视频说清整个英语语法体系(重塑你的语法认知框架)

前言 绝大多数句子&#xff1a;什么 怎么样 几乎所有的英语句子&#xff1a;主语谓语 广义 “动作”&#xff1a;语法上的“动词” 主语(人或物) 谓语(“动作”发生了什么事情) 有哪些”动作“(动词)&#xff1f; 可以独立完成的动作&#xff1a;不及物动词 [主语不及物…

GEE学习笔记 七十九:【GEE之Python版教程十一】

列表在python中就是使用中括号包围的数据&#xff0c;比如[11,23,10]等。列表&#xff08;list&#xff09;是可变的&#xff0c;同时在python中还有集合&#xff08;set&#xff09;以及元组&#xff08;tuple&#xff09;和这个类似&#xff0c;不要把这些内容搞混。 运行下…

Maven怎样构建生命周期?

项目构建生命周期Maven的本质是一个项目管理工具&#xff0c;将项目开发和管理过程抽象成一个项目对象模型(POM)。Maven构建生命周期描述的是一次构建过程经历经历了多少个事件。对项目构建的生命周期划分为3套&#xff0c;其中clean负责清理工作&#xff0c;default负责核心工…

Git小乌龟每次推送拉取都弹窗和用户名密码报错(解决办法)

目录 一、小乌龟推送代码到云端用户名和密码报错 &#xff08;一&#xff09; 遇到问题 &#xff08;二&#xff09;解决办法 二、小乌龟每次推送拉取都要输入账号和密码 &#xff08;一&#xff09;遇到问题 &#xff08;二&#xff09;解决办法 一、小乌龟推送代码到云…

AlphaGo 和 ChatGPT有何相似之处? 附AlphaGo核心算法开源链接

AlphaGo 和 ChatGPT 是迄今为止最著名、最具开创性的两个 AI 系统之一。尽管它们被设计用于不同的目的&#xff0c;但它们共享一些重要的相似之处&#xff0c;包括使用深度学习、神经网络以及专注于达到人类水平表现等。而不久前&#xff0c; DeepMind 悄悄开源了AlphaGo的核心…

在windows安装MySQLworkbench

跑到官网安装MySQL :: MySQL Downloads select MySQL community server Recommended Download download the first installer here just start my download let’s open it and agree with the license agreement 安装&#xff0c;一直点next点到Account and Rolse 自己定密码…

C++学习笔记-变量类型

变量为我们提供了程序可以操作的命名存储。 C 中的每个变量都有一个特定的类型&#xff0c;它决定了变量内存的大小和布局; 可存储在该内存中的值范围; 以及可以应用于变量的操作集。 变量的名称可以由字母&#xff0c;数字和下划线字符组成。 它必须以字母或下划线开头。 大写…

【C语言每日一题】——倒置字符串

【C语言每日一题】——倒置字符串&#x1f60e;前言&#x1f64c;倒置字符串&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者简…

基于蜣螂算法改进的LSTM分类算法-附代码

基于蜣螂算法改进的LSTM分类算法 文章目录基于蜣螂算法改进的LSTM分类算法1.数据集2.LSTM模型3.基于蜣螂算法优化的RF4.测试结果5.Matlab代码摘要&#xff1a;为了提高LSTM数据的分类预测准确率&#xff0c;对LSTM中的参数利用蜣螂搜索算法进行优化。1.数据集 数据的来源是 UC…

让我们,从头到尾,通透I/O模型

什么是IO 一句话总结 IO就是内存和硬盘的输入输出 I/O 其实就是 input 和 output 的缩写&#xff0c;即输入/输出。 那输入输出啥呢&#xff1f; 比如我们用键盘来敲代码其实就是输入&#xff0c;那显示器显示图案就是输出&#xff0c;这其实就是 I/O。 而我们时常关心的磁盘…