跳到主要内容

js 只是语言,真正能让页面起来的是 DOM、BOM,夹杂着交互产生的事件。

一、 事件流

事件流是描述事件从产生到被处理的完整过程

  • 捕获阶段:事件从最外层的根结点(window)开始,逐层向内传播到目标元素的父节点(不包含目标元素)
  • 目标阶段:事件到达实际触发的目标元素,执行该元素上的事件监听器
  • 冒泡阶段:事件从目标元素开始,逐层向外传播回根结点(window

通过 addEventListener(type, listener, useCapture) 注册事件,useCapture 参数控制监视器在那个阶段执行

  • useCapture = true:监听器在捕获阶段触发
  • useCapture = false:监听器在冒泡阶段触发
注意
  • 并非所有的事件都支持冒泡(如 focusblur ,需用 focusinfocusout 代替已支持冒泡)
  • event.target 是实际触发事件的元素,event.currentTarget 是当前执行监听器的元素(常用于事件委托)
  • removeEventListeneruseCapture 参数必须和注册时一致,否则无法移除(匿名函数无法移除,因引用不同)

二、 事件委托

事件委托(事件代理)利用事件冒泡机制,将子元素的事件监听器统一绑定在父元素上,由父元素处理子元素的事件。

  • 子元素触发事件后,事件会冒泡到父元素
  • 父元素通过 event.target 判断具体触发事件的子元素,再执行对应的逻辑
注意
  • 目标元素判断不准确:若子元素嵌套, e.target 可能是子元素的子元素而非期望的子元素,需使用 e.target.closest('xx') 向上查找最近的目标子元素
  • 误阻止冒泡:子元素调用了 event.stopPropagation() 阻止了事件的进一步冒泡,会导致父元素的委托监听器无法执行

三、 DOM 节点的操作

DOM 节点操作是指对 HTML 元素节点进行 创建添加移除复制替换查找等操作,是动态修改页面结构的基础。

操作方法说明
创建节点document.createElement(tag)创建元素节点
document.createTextNode(text)创建文本节点
查找节点getElementById最快,没有之一。但 ID 值需要唯一
getElementByClassName返回 HTMLCollection(返回类数组,动态)
getElementsByTagName返回 HTMLCollection(返回类数组,动态)
querySelector/querySelectorAll支持 CSS 选择器,灵活但性能略低(返回静态列表)
添加元素parent.appendChild(child)将子节点添加到父节点的末尾(若子节点已存在于文档树,会移动位置)
parent.insertBefore(newChild, ref)ref 节点前插入 newChild 节点
parent.append(...nodes)将一个或多个节点或字符串添加到末尾
parent.prepend(...nodes)将一个或多个节点或字符串添加到开头
node.after(...nodes)在节点之后插入
node.before(...nodes)在节点之前插入
node.replaceWith(...nodes)用新节点替换当前节点
移除节点parent.removeChild(child)通过父节点移除子节点 (返回被移除的节点)
child.remove()节点自身调用删除(IE 不支持,需调用 removeChild 兼容)
复制节点node.cloneNode(deep)deep=true 复制节点及所有子节点;deep=false 仅复制节点本身(无事件)
替换节点parent.replaceChild(new, old)使用 new 节点替换掉 old 的节点
修改节点element.innerHTML获取或设置元素的 HTML 内容。注意 XSS 风险
element.textContent获取或设置元素的纯文本内容。注意 XSS 风险
element.innerText获取或设置元素的渲染后的文本内容
  • childNodes:返回所有的子节点(含文本、注释等,可能包含空白文本节点,如换行符)
  • children:仅返回元素子节点(更常用,避免空白节点的干扰)
  • firstChild 🆚 firstElementChild:前者返回的是第一个节点(可能是文本),后者返回的是第一个元素子节点
  • nextSibling 🆚 nextElementSibling:前者返回下一个兄弟节点(可能是文本),后者返回下一个元素兄弟节点
注意
  • 空白节点干扰childNodes 包含换行/空格产生的文本节点,循环时需过滤(如 node.nodeType === 1 只处理元素节点)
  • 克隆节点丢失事件cloneNode 不会复制通过 addEventListener 绑定的事件(若需要需重新监听及移除)
  • 删除节点后的引用残留:删除节点后若仍保留引用,可能导致内存泄漏(需手动设置为 null
  • 创建不等于添加:动态创建的元素需要通过 appendChildinsertBefore 插入 DOM 树后才会显示
  • 减少频繁操作 DOM:频繁操作 DOM 时,建议先创建文档片段(document.createDocumentFragment())批量处理,再一次性插入,减少重绘/回流

四、BOM ! 炸了

BOM 是 Browser Object Model 的缩写,及浏览器对象模型,它提供了独立于网页内容而与浏览器窗口进行交互的 API。

  • window 对象是 JavaScript 中的顶层对象
  • navigator 包含客户端浏览器的信息
  • screen 包含客户端显示屏的信息
  • history 包含浏览器窗口访问过的 url 信息
  • location 包含当前网页文档的 url 信息
  • document 包含整个 HTML 文档,可被用来访问文档内容,及页面所有的页面元素
注意
  • replaceassign 混淆replace 会清除当前历史记录,用户无法返回上一页,需谨慎使用
  • pushState 导致 404:SPA 中使用 pushState 修改 URL 后,直接刷新页面可能会因服务器没有对应的路由而返回 404。需服务器配置支持(如 Nginx 转发到 index.html)
  • popstate 事件误解pushState/replaceState 不会触发 popstate,仅在用户点击前进/后退或调用 back()/forward()/go() 时触发