Web Component渲染元素删除异常:数组更新但页面未重渲染
Web Component 删除按钮不触发重新渲染的问题修复
核心问题分析
filter是纯函数,不会修改原数组,你当前只调用了filter但没将返回值赋值给原数组,导致window.selectedItems.items实际没有变化。SelectedProducts的render方法用forEach循环每次覆盖innerHTML,最终页面只会显示最后一个选中项。DeleteButton在构造函数中绑定点击事件时,组件还未挂载到DOM,按钮元素不存在,导致事件绑定失效。SelectBtn里的name变量未定义,应该从元素的name属性获取值。
修复方案
1. 修正数组更新逻辑
在DeleteButton的handleDelete中,将filter返回的新数组赋值给原数组,确保数据源同步:
// 直接修改全局数据源 window.selectedItems.items = window.selectedItems.items.filter(item => item.id !== productId);
2. 修复批量渲染逻辑
SelectedProducts的render方法应该先拼接所有项的HTML,再一次性赋值,避免循环覆盖:
render() { // 拼接所有选中项的HTML const itemsHtml = this.items.map((item) => ` <div style="display:flex; align-items: center; background-color:white; padding:15px; margin-bottom:8px;"> <span>商品: ${item.name} | 数量: ${item.qty}</span> <delete-button> <button data-id="${item.id}" class="delete-btn"> Delete me </button> </delete-button> </div> `).join(''); this.innerHTML = itemsHtml; }
3. 正确绑定事件到组件生命周期
DeleteButton的事件绑定应该放在connectedCallback中,确保组件挂载后再获取元素:
class DeleteButton extends HTMLElement { constructor() { super(); this.selectedProducts = document.querySelector('selected-products'); } connectedCallback() { // 组件挂载后再绑定事件 this.querySelectorAll('button').forEach((button) => button.addEventListener('click', this.handleDelete.bind(this)) ); } }
4. 修正SelectBtn的变量未定义问题
从元素属性获取id和name,避免使用未定义变量:
handleSelect(e) { e.preventDefault(); const itemId = this.getAttribute('id'); const itemName = this.getAttribute('name'); const itemObj = { id: itemId, name: itemName, qty: 1, }; // ...后续逻辑不变 }
修改后的完整代码
JavaScript部分
class DeleteButton extends HTMLElement { constructor() { super(); this.selectedProducts = document.querySelector('selected-products'); } connectedCallback() { this.querySelectorAll('button').forEach((button) => button.addEventListener('click', this.handleDelete.bind(this)) ); } handleDelete(e) { e.preventDefault(); let productId = e.currentTarget.dataset.id; // 更新全局数据源 window.selectedItems.items = window.selectedItems.items.filter(item => item.id !== productId); // 触发重新渲染 this.selectedProducts.render(); } } customElements.define('delete-button', DeleteButton); class SelectedProducts extends HTMLElement { constructor() { super(); this.items = window.selectedItems.items; } render() { const itemsHtml = this.items.map((item) => ` <div style="display:flex; align-items: center; background-color:white; padding:15px; margin-bottom:8px;"> <span style="margin-right:15px;">商品: ${item.name} | 数量: ${item.qty}</span> <delete-button> <button data-id="${item.id}" class="delete-btn"> Delete me </button> </delete-button> </div> `).join(''); this.innerHTML = itemsHtml; } } customElements.define('selected-products', SelectedProducts); class ProductCard extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open', }); this.shadowRoot.innerHTML = `<style> ::slotted(div){ color: #4B5563; font-weight: 900; text-align: center; font-size: 20px; } </style> ` + ` <div style="background: white; margin-right: 15px; padding:10px;"> <slot name="button"></slot> <slot name="img"></slot> </div> `; } } customElements.define('product-card', ProductCard); class SelectBtn extends HTMLElement { constructor() { super(); this.itemsPicked = document.querySelector('selected-products'); this.attachShadow({ mode: 'open', }); this.shadowRoot.innerHTML = ` <button aria-label="Select" type="button" class="pressed" data-addbtn="add-btn" style="padding:8px 16px; cursor:pointer;" > + 选择商品 </button> `; this.shadowRoot .querySelectorAll('button') .forEach((button) => button.addEventListener('click', this.handleSelect.bind(this))); } handleSelect(e) { e.preventDefault(); const itemId = this.getAttribute('id'); const itemName = this.getAttribute('name'); const itemObj = { id: itemId, name: itemName, qty: 1, }; const item_stored = window.selectedItems.items.find((item) => item.id == itemObj.id); if (item_stored) { item_stored.qty++; } else { window.selectedItems.items.push(itemObj); } this.itemsPicked.render(); } } customElements.define('select-button', SelectBtn);
HTML部分
<body> <div style="display: flex; justify-content: center; align-items: center; background: lightblue; padding: 20px"> <product-card> <div slot="button"> <select-button id="1" name="商品A"></select-button> </div> <div slot="img"> <div style="height: 100px; width: 100px; background:#f0f0f0; display:flex; align-items:center; justify-content:center;">商品A</div> </div> </product-card> <product-card> <div slot="button"> <select-button id="2" name="商品B"></select-button> </div> <div slot="img"> <div style="height: 100px; width: 100px; background:#f0f0f0; display:flex; align-items:center; justify-content:center;">商品B</div> </div> </product-card> <div style="margin-left:20px;"> <h3>已选商品</h3> <selected-products></selected-products> </div> </div> <script src="app.js"></script> <script> window.selectedItems = { items: [] }; </script> </body>
内容的提问来源于stack exchange,提问作者Mike Haslam
相关产品推荐
相关产品推荐

