slot标签仅在shadow dom中生效,必须通过自定义元素attachshadow后插入含slot的模板,且name与slot属性需精确匹配、内容须为直系子节点,时机上需监听slotchange事件获取assignednodes。

slot 标签写在 Light DOM 里完全无效
浏览器会把没挂 Shadow DOM 的 <slot></slot> 当成普通标签忽略,不报错、不警告、也不渲染。常见错误是直接在页面中写:<div><slot name="header"></slot></div>,或者塞进 <div> 里当子元素用——这种写法永远不生效。
<p>必须先注册合法自定义元素,再在 <code>constructor 或 connectedCallback 中调用 this.attachShadow({ mode: 'open' });之后把含 <slot></slot> 的模板(比如 <template></template>)显式插入到 this.shadowRoot 中。
- 不能对
<div>、<code><p></p>等原生元素调用attachShadow(),否则抛出DOMException: This element does not support shadow roots -
{ mode: 'closed' }虽能渲染,但 JS 无法通过this.shadowRoot.querySelector('slot')访问节点,调试时容易误判为“没生效” - 模板插入必须显式执行:
this.shadowRoot.appendChild(template.content.cloneNode(true)),光写<template></template>标签不等于自动加载 - 没带
slot属性的元素(如<p>正文</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5117" title="HTML Extract"><img src="https://img.php.cn/upload/skill/000/000/081/179033952939354.jpg" alt="HTML Extract" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill5117" title="HTML Extract" class="overflowclass">HTML Extract</a> <p class="overflowclass">使用 MinerU 从 HTML 页面和文件中提取内容,将 HTML 转换为保持标题、列表、表格及文本层次结构的干净、结构化 Markdown。F...</p> </div> <a rel="nofollow" href="/xiazai/skill5117" title="HTML Extract" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>)只尝试填入第一个无name的<slot></slot>,后面再多空<slot></slot>也收不到 - 多个同名
<slot name="item"></slot>时,每个slot="item"元素按顺序填入一个 slot,不是广播复制 -
<slot><p>缺省文案</p></slot>的 fallback 仅在**完全没有节点分配进来**时显示;哪怕只有一个空文本节点,fallback 就不会出现 -
assignedNodes()默认不展开嵌套结构,要拿到所有最终投射进来的节点(包括 fallback 里的),必须加{ flatten: true } -
assignedElements()只返回元素节点,适合需要过滤纯文本的场景 - 这些方法返回的是实时快照,不是响应式引用;DOM 变更后需重新调用
- 投射本质是“投影”,不是 DOM 移动:内容始终保留在 Light DOM 中,
::slotted()只是给它加样式层 - 事件冒泡路径走的是 Light DOM,监听点击得在 Light DOM 节点上绑,不能只查
shadowRoot - HTML 原生
<slot></slot>不支持作用域插槽,也不传递数据;它只做一件事:按规则把 Light DOM 节点投射到占位位置
具名插槽 name 和 slot 属性必须严格字符串匹配
<slot name="header"></slot> 只接收 <h2 slot="header"></h2>,哪怕多一个空格、大小写错一位、或拼成 slot="Header" 都不匹配。这不是模糊查找,是精确比对。
外部内容还必须是自定义元素的**直系子节点**:<my-card><h2 slot="header">标题</h2></my-card> ✅;<my-card><div><h2 slot="header">标题</h2></div></my-card> ❌ —— <h2></h2> 是孙节点,被直接丢弃。
查 assignedNodes() 返回空?别急着改代码,先等 slotchange
在 connectedCallback 里立刻调 slot.assignedNodes(),大概率拿到空数组——因为此时 Light DOM 内容还没完成分发。这是最常被忽略的时机问题。
可靠做法是监听 slotchange 事件:slot.addEventListener('slotchange', () => { console.log(slot.assignedNodes({ flatten: true })); })。注意:slotchange 不冒泡、不捕获,也不能在 shadowRoot 上监听,必须绑定到具体 <slot></slot> 元素上。
::slotted() 样式穿透只支持单层选择器
::slotted(*) 能样式化投射进来的内容,但它只作用于直接子元素,不支持后代选择器。比如 ::slotted(.header h2) 无效,::slotted(.header) > h2 也无效。
想控制深层结构,得靠 CSS 自定义属性穿透,或在 Light DOM 里预设好类名,再由 Shadow DOM 外部统一管理样式逻辑。










