如何将fetch获取的数据传入反引号模板字符串内的onclick函数
问题原因
报错由两个问题共同导致:
- 代码存在笔误:
entlet list = "";为拼写错误,需修正为let list = ""; - 核心逻辑错误:在模板字符串中直接将JS对象
song插值到内联onclick属性时,对象会被默认转换为字符串[object Object],最终渲染出的事件代码为makeAllplays([object Object]),不符合JS语法规范,浏览器解析时就会抛出Unexpected identifier错误。
解决方案
推荐优先使用事件委托的方式处理动态生成元素的点击事件,避免内联事件的转义、作用域问题,代码可维护性更强:
方案1:事件委托(推荐)
- 修正笔误,渲染DOM时给每个歌曲项添加存储歌曲唯一标识(数组索引/歌曲ID)的自定义属性,移除内联onclick绑定:
const res = await fetch('./songApi/songs.json') const fetchedSongs = await res.json() let list = ""; // 单纯做遍历拼接不需要用map,forEach语义更合适 fetchedSongs.forEach((song, index) => { list += ` <div class="songitem" style="background:${song.background} ;" data-song-index="${index}"> <img src="${song.coverpath}" alt="${song.SongName}"> <span class="name">${song.SongName}</span> <span class="songlistplay"> <span class="songTime">${song.time}</span> <i class="far fa-play-circle songitemplay"></i> </span> </div>` }) // 将拼接完成的列表插入到页面对应容器,示例容器class为song-list-wrap document.querySelector('.song-list-wrap').innerHTML = list
- 给歌曲列表的父容器绑定一次点击事件,通过事件委托匹配播放按钮,拿到对应歌曲数据后调用播放逻辑:
// 父容器绑定点击事件,后续动态新增的歌曲项也能生效 document.querySelector('.song-list-wrap').addEventListener('click', (e) => { // 判断点击目标是否为播放按钮 if (e.target.classList.contains('songitemplay')) { // 向上查找最近的歌曲项元素,读取存在自定义属性上的索引 const songItem = e.target.closest('.songitem') const songIndex = Number(songItem.dataset.songIndex) // 拿到对应歌曲数据 const currentSong = fetchedSongs[songIndex] // 执行播放逻辑 makeAllplays(currentSong) } }) // 播放函数正常接收歌曲对象参数即可 function makeAllplays(song) { console.log('当前播放歌曲:', song) // 补充你的播放逻辑 }
方案2:内联事件传参(不推荐,易出现转义bug)
如果一定要保留内联onclick写法,不能直接传入对象,可传入歌曲索引/ID作为唯一标识,在函数内部回查完整歌曲数据:
fetchedSongs.forEach((song, index) => { list += ` <div class="songitem" style="background:${song.background} ;"> <img src="${song.coverpath}" alt="${song.SongName}"> <span class="name">${song.SongName}</span> <span class="songlistplay"> <span class="songTime">${song.time}</span> <i class="far fa-play-circle songitemplay" onClick="makeAllplays(${index})" ></i> </span> </div>` }) // 对应函数修改为接收索引参数 function makeAllplays(songIndex) { const currentSong = fetchedSongs[songIndex] // 补充播放逻辑 }
注意:如果尝试直接传入JSON序列化后的对象,必须手动转义引号,否则会破坏HTML属性结构,兼容性很差,非必要不使用该写法。
额外优化点
- 插值到HTML属性的变量必须包裹引号,比如
src="${song.coverpath}",如果路径包含空格、特殊字符会导致属性解析错误 - 图片alt属性不要写死固定值,替换为当前歌曲名
song.SongName更符合可访问性规范 - 没有返回值需求的遍历操作不要用
map,map是用来生成新数组的方法,单纯做遍历拼接用forEach或普通for循环更合理
内容的提问来源于stack exchange,提问作者Rohit Barate
相关产品推荐
相关产品推荐

