如何将EJS数据干净地传递给JavaScript变量?
最干净的实现方案
用HTML原生自定义数据属性(data-*)绑定数据
这是HTML标准定义的、专门用于元素存储自定义交互数据的原生能力,根本不需要额外加隐藏的DOM节点,数据和触发交互的按钮直接强绑定,完全不用写DOM遍历、找父级子级的杂乱逻辑。
只需要修改现有EJS代码里的加购按钮,把需要的产品数据直接作为标准属性挂在按钮上:
<div class="productSection"> <% products.forEach(function(product){ %> <div class="card"> <div class="card-title"><%- product.name %></div> <div class="card-body"> <div class="card-body-price"><%- product.price %></div> </div> <!-- 直接把产品核心数据绑定到加购按钮上 --> <button class="card-cartBtn" data-product-id="<%= product._id %>" >+</button> </div> <% }) %> </div>
对应的JS逻辑不用给每个按钮单独绑事件,直接用事件委托监听整个产品区域的点击,靠原生的datasetAPI直接从点击的按钮上读数据就行:
document.querySelector('.productSection').addEventListener('click', (e) => { // 只处理加购按钮的点击 if (e.target.classList.contains('card-cartBtn')) { // 直接拿按钮上存的product._id,完全不用爬DOM层级 const productId = e.target.dataset.productId // 直接传值给购物车逻辑就行 addToCart(productId) } })
这种方式的好处非常直接:
- 没有任何多余的隐藏DOM,交互数据和触发交互的元素一一对应,逻辑扫一眼就懂,不会出现找半天数据藏在哪个父节点的情况
- 原生API取值,后续哪怕调整DOM结构(比如给按钮加几层包裹元素),取值逻辑也不会轻易失效
- 用事件委托的模式,哪怕后续动态加载新的产品卡片,也不需要给新按钮重新绑事件,性能更好
- 输出属性值用
<%= %>做HTML转义,能避免特殊字符截断属性、引发XSS的风险
如果需要传递的产品字段很多,把所有字段都堆在data-*属性上会让DOM代码臃肿,就换个更清爽的方式:把服务端拿到的产品数据整体序列化成合法JSON,直接注入到页面的JS全局作用域里:
<script> // 序列化产品数据存入全局变量,转义尖括号防止XSS window.PRODUCTS = <%- JSON.stringify(products.map(p => ({ id: p._id, name: p.name, price: p.price })).replace(/<\//g, '<\\/') %> </script>
这种情况下按钮上只需要存一个核心的product id,点击时直接从全局数组里匹配对应的完整产品数据就行,不用把全量字段都铺在DOM上。
要明确一点:标准的
data-*自定义属性是浏览器原生支持的规范能力,和你反感的"写display:none的隐藏div存数据"的野路子完全不是一回事,是业内通用的干净实现,不存在杂乱的问题。
内容的提问来源于stack exchange,提问作者DonSeverino
相关产品推荐
相关产品推荐

