塔罗牌Web应用点击触发Uncaught TypeError: Property 'handleEvent' is not callable错误求助
解决"Uncaught TypeError: Property 'handleEvent' is not callable"错误及塔罗牌抽卡功能修复
问题根源
- 事件监听绑定错误:
cardb.addEventListener("click", changeCard("a"))这种写法会立即执行changeCard("a"),而非将函数作为事件处理函数传递。由于changeCard没有返回值,实际传给监听的是undefined,浏览器尝试调用undefined的handleEvent方法,触发报错。 - 函数提前终止:
jackandjill函数开头的return语句直接终止了函数执行,后续的changeCard定义、事件绑定代码完全未运行。 - eval滥用:用
eval获取DOM元素是不良实践,容易引发错误和安全问题。 - 重复请求JSON:每次抽卡都重新请求本地JSON,效率低下。
- 无效的setTimeout:
setTimeout(500)不符合语法要求,正确用法需要传入函数作为第一个参数。
修复方案
1. 正确绑定事件监听
将changeCard改为返回函数的形式,确保传递给addEventListener的是可调用的函数:
function changeCard(inputLetter) { const cards = {a: carda, b: cardb}; const card = cards[inputLetter]; return async function() { // 抽卡逻辑... } } // 绑定事件时,传递changeCard返回的函数 cardb.addEventListener("click", changeCard("a"));
2. 移除无效的return语句
删掉jackandjill函数开头的return,让后续代码正常执行。
3. 替换eval,使用对象映射
用对象存储DOM元素的映射,替代eval:
const cards = {a: carda, b: cardb}; const card = cards[inputLetter];
4. 预加载并缓存JSON数据
页面初始化时一次性加载塔罗牌数据,避免重复请求:
let tarotCards = null; async function loadTarotData() { const response = await fetch('tarotdeck.json'); const data = await response.json(); tarotCards = data.cards; }
5. 修正setTimeout或移除无用延迟
如果不需要延迟执行,直接删掉无效的setTimeout;如果需要延迟,改为正确写法:
setTimeout(() => { // 要延迟执行的代码 }, 500);
完整修正代码
JavaScript部分
let carda = document.querySelector("div.carda"); let cardb = document.querySelector("div.cardb"); let cardName = document.querySelector("h3.name"); let moreInfo = document.querySelector("p.about"); // 预加载塔罗牌数据,只加载一次 let tarotCards = null; async function loadTarotData() { const response = await fetch('tarotdeck.json'); const data = await response.json(); tarotCards = data.cards; } async function drawCard(index) { if (!tarotCards) await loadTarotData(); return Object.values(tarotCards[index]); } function getRandomNumber(min, max) { let step1 = max - min + 1; let step2 = Math.random() * step1; let result = Math.floor(step2) + min; return result; } // 重构changeCard,返回一个事件处理函数 function changeCard(inputLetter) { // 用对象替代eval const cards = {a: carda, b: cardb}; const card = cards[inputLetter]; return async function() { const ranIndex = getRandomNumber(0, 78); const cardDrawn = await drawCard(ranIndex); cardName.innerHTML = cardDrawn[0]; moreInfo.innerHTML = cardDrawn[1]; card.classList.remove("default-styling"); cardName.classList.remove("hide"); moreInfo.classList.remove("hide"); } } // 页面加载完成后绑定事件 window.addEventListener('DOMContentLoaded', async () => { // 先加载数据 await loadTarotData(); // 绑定事件,这里传递的是changeCard返回的函数 cardb.addEventListener("click", changeCard("a")); carda.addEventListener("click", changeCard("b")); });
HTML部分(修正id拼写错误)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Daily Tarot</title> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous" /> <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"> <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"> <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"> <link rel="manifest" href="/site.webmanifest"> </head> <body> <div class="app_container"> <h1>Tarot</h1> <div id="carda"> <div class="carda default-styling"> </br> </div> <div id="cardb"> <div class="cardb default-styling"> </br> </div> <h3 class="name hide">Name of card</h3> <p class="about hide">About card</p> <h3 class="arcana hide">arcana card</h3> <p class="suit hide">suit card</p> <p class="fortune hide">Fortune telling</p> <p class="keywords hide"> keywords </p> <p class="meanings hide"> meanings </p> <p class="Archetype hide">Archetype </p> <p class="Hebrew hide">Hebrew Alphabet </p> <p class="Numerology hide">Numerology </p> <p class="Elemental hide">Elemental </p> <p class="Mythic hide">Mythical/Spiritual </p> <p class="Questions hide"> Questions to Ask </p> </div> </div> </div> <small>The images and information about each card are from <a href="https://www.tarot.com/tarot/cards">Tarot.com</a><br /><a href="https://github.com/SamJamWillingham/Tarot-Card-Drawer" >Open course code</a> </small> <script src="script.js"></script> </body> </html>
CSS部分
div.app_container { max-width: 600px; border-radius: 8px; margin: 15px auto; background-image: linear-gradient(-225deg, #e3fdf5 0%, #ffe6fa 100%); box-shadow: 8px 8px 10px rgba(0, 0, 0, 0.281); padding: 25px; } div.default-styling { display: block; margin: 0 auto; color: #000000; background-color: aqua; /*Delete me!*/ text-align: center; border: 1px solid #000000; border-radius: 10px; background-color: rgba(255, 255, 255, 0.1); max-width: 220px; padding-top: 125px; padding-bottom: 125px; } img.cardImg{ display: block; margin: 0 auto; max-width: 189px; height: 320px; } div.newCard, .carda, .cardb{ display: block; margin: 0 auto; max-width: 210px; border: none; background-color: red; /*Delete me!*/ border-radius: 15px; } #cardb{ float: right; width: 300px; height: 300px; } .cardb{ float: left; height: 320px; width: 300px; } h1 { font-family: "cursive"; text-align: center; } h3, p { font-family: "sans-serif"; text-align: left; } small { display: block; margin: 0 auto; text-align: center; } .hide { display: none; }
内容的提问来源于stack exchange,提问作者NicoleHaru
相关产品推荐
相关产品推荐

