自定义元素动态生成异常:需在创建阶段设置属性
你遇到的核心问题是:自定义元素的constructor在document.createElement调用时就会立即执行,此时你还没调用setAttribute设置src和href属性,所以this.getAttribute("src")和this.getAttribute("href")返回的都是null,导致Shadow DOM里的图片和链接无法正确渲染,最终只显示CSS定义的空方框。
窗口 resize 后异常,是因为重新执行createDivItems时,重复了同样的错误流程——先创建元素(触发constructor,此时属性未设置),再设置属性,但constructor已经执行过,不会重新触发渲染。
1. 修改自定义元素代码,监听属性变化
自定义元素提供了attributeChangedCallback生命周期钩子,用来监听属性变化,当属性更新时自动重新渲染内容。同时需要通过observedAttributes指定要监听的属性:
class Item extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open'}); // 初始化渲染(此时属性可能未设置,显示默认值) this.render(); } // 指定需要监听的属性列表 static get observedAttributes() { return ['src', 'href']; } // 属性变化时触发,重新渲染内容 attributeChangedCallback(name, oldValue, newValue) { if (oldValue !== newValue) { this.render(); } } // 提取渲染逻辑为独立方法,方便复用 render() { const src = this.getAttribute("src"); const link = this.getAttribute("href"); const text = this.textContent.trim(); this.shadowRoot.innerHTML = ` <style>/* Styles excluded */</style> <div> <a href='${link || '#'}'> <div class="bg"> <img src="${src || ''}" alt="${text || 'Item'}"> </div> <p>${text || 'not used.'}</p> </a> </div> `; } } customElements.define('div-item', Item);
2. 修正动态创建元素的代码
document.createElement的第二个参数不是用来传递属性的(它仅用于自定义元素的is选项等场景),直接删除无效的第二个参数,保留setAttribute逻辑即可:
function createDivItems() { var rows = document.getElementsByClassName("row"); for (var j = 0; j < rows.length; j++) { var row = rows[j]; row.innerHTML = ''; for (var i = 0; i < (window.innerWidth - 60); i += 120) { // 正确创建自定义元素,无需在第二个参数传属性 var elem = document.createElement("div-item"); // 设置属性,此时会触发attributeChangedCallback重新渲染 elem.setAttribute("src", '<?php echo "assets/not found.png"; ?>'); elem.setAttribute("href", '<?php echo "/newPage.php"; ?>'); elem.textContent = 'not used.'; row.appendChild(elem); } } }
额外说明:延迟显示正常的原因
延迟执行时,相当于先创建元素并添加到DOM,之后再设置属性,此时attributeChangedCallback会被触发,触发重新渲染,所以内容能正常显示。但窗口resize时重新执行createDivItems,又回到了先创建元素(constructor执行时属性为空)再设置属性的流程,没有attributeChangedCallback的话,constructor不会再次执行,因此仍然显示空方框。
内容的提问来源于stack exchange,提问作者Slothscript

