firstelementchild常返回null是因为它只匹配元素节点,会跳过html中换行、空格等生成的文本节点;应优先用children[0]或逐层判空访问。

firstElementChild 为什么常返回 null 而不是你想要的元素
因为 firstElementChild 只找「元素节点」,跳过所有空白文本节点和注释节点。HTML 源码里标签间的换行、缩进、空格,都会被解析成 #text 节点——它算子节点,但不算子元素。
常见错误现象:document.getElementById("container").firstElementChild 返回 null,但你知道里面明明有个 <div>。<ul>
<li>检查 DOM 结构:用浏览器开发者工具的「Elements」面板右键 → 「Edit as HTML」,看是否在第一个真实元素前有换行或空格</li>
<li>对比 <code>firstChild:如果 firstChild.nodeName === "#text",那就证实是空白干扰了
children[0],它和 firstElementChild 行为一致,且兼容性略好(IE9+)lastElementChild 在动态插入内容后失效的典型场景
当你用 appendChild() 或 insertAdjacentElement() 新增一个子元素,lastElementChild 通常能立刻反映新状态。但如果你插入的是字符串(如 innerHTML += "<li>new</li>"),就可能出问题:
-
innerHTML会先清空再重建整个子树,原有节点引用全部失效,lastElementChild指向的是新生成的节点,但旧变量还存着已卸载的节点 - 若插入内容含开头/结尾空白(比如
"\n<li>item</li>\n"),重建后lastElementChild仍可能为null(因末尾换行生成了#text) - 避免混合使用:不要一边用
innerHTML更新,一边依赖lastElementChild做后续操作;统一用appendChild()或children[children.length - 1]
firstElementChild 和 children[0] 到底该选哪个
两者在绝大多数现代环境里行为完全等价,但细微差别影响调试和兼容性判断:
-
firstElementChild是只读属性,语义清晰,适合表达“我要第一个元素”的意图 -
children[0]是 HTMLCollection 的索引访问,返回值类型相同,但多一次属性查找(children)+ 数组取值,性能差异可忽略 - IE9 不支持
firstElementChild,但支持children;不过现在基本无需考虑 IE9 - 真正容易踩的坑是误以为
children是数组——它其实是类数组(HTMLCollection),不能直接用map或展开语法,得先转成数组:[...element.children]
用 lastElementChild 获取内容时遇到 text 为空的排查路径
比如你写 element.lastElementChild.textContent,结果是空字符串,但元素明明有文字。这不是属性问题,而是节点层级理解偏差:
-
lastElementChild找的是「最后一个直接子元素」,不是最深的那个文本节点 - 例如
<div><p>hello <span>world</span></p></div>,div.lastElementChild是p元素,它的textContent是"hello world";但如果p里还有其他元素(比如图标<i></i>),而文字被包在span里,那p.lastElementChild就是span,你需要继续向下取 - 别链式调用太长:避免
el.lastElementChild.lastElementChild.textContent这种写法——任何一环为null就报错,应逐层判空或用可选链?.textContent - 如果目标只是取最后一块可见文本,
el.textContent.trim().split(/\s+/).pop()更鲁棒(但语义不同,慎用)
实际项目中,firstElementChild 和 lastElementChild 最常被忽略的复杂点在于:它们只作用于直接子元素,不递归,也不管样式是否显示(display: none 的元素仍会被计入)。想跳过隐藏元素或找“视觉上最后一个”,就得自己遍历 children 并检查 offsetParent。











