Why is requestAnimationFrame generating "Uncaught TypeError: Cannot read property 'id' of undefined"?
Why is requestAnimationFrame generating "Uncaught TypeError: Cannot read property 'id' of undefined"?
我是 JavaScript 初学者,我有一些代码在功能方面工作得很好,但它是如此 not DRY 和 "dirty"...光是看那段代码就让我恶心。
所以,我决定做一个非常漂亮、现代、干净和干燥的版本。
但是现在我完全被困住了,尽管到处搜索都找不到任何东西来理解我到底做错了什么?
最重要的是:为什么错了?
因此,这是当前代码:
document.addEventListener("DOMContentLoaded", function() {
// parent div that holds the navigation wheel:
var navDiv = document.querySelector("#wrapper > section > div");
navDiv.addEventListener("mousedown", moveEye, false);
navDiv.addEventListener("mouseup", stopEye, false);
var eye = document.querySelector("#eyeball"),
iris = document.querySelector("#iris"),
reqID;
function moveEye(e) {
var btnClicked = e.target.id;
// alert("Button clicked: " + btnClicked);
if (btnClicked === "right") {
eye.style.left = (eye.offsetLeft += 3) + "px";
} else if (btnClicked === "left") {
eye.style.left = (eye.offsetLeft -= 3) + "px";
} else if (btnClicked === "top") {
eye.style.top = (eye.offsetTop -= 3) + "px";
} else if (btnClicked === "bottom") {
eye.style.top = (eye.offsetTop += 3) + "px";
}
reqID = window.requestAnimationFrame(moveEye);
}
function stopEye(e) {
cancelAnimationFrame(reqID);
}
}, false);
<div id="wrapper">
<p>Description comes here.</p>
<section>
<p>Navigation Wheel:</p>
<div>
<span id="top"></span>
<span id="bottom"></span>
<span id="left"></span>
<span id="right"></span>
<span id="hub"></span>
</div>
</section>
<section id="stage">
<figure id="eyeball">
<span id="shadow"></span>
<span id="iris" class="irisMove"></span>
</figure>
</section>
</div>
请注意,我特别想保留这个:
var navDiv = document.querySelector("#wrapper > section > div");
因为我想尽可能保持代码干爽和干净。
这就是为什么我选择包含 4 个按钮的 parent div
而不是 div 中的 4 个按钮中的每一个。
所以,我想听父 div 告诉我当前正在单击里面的 4 个按钮中的哪一个(即 4 个跨度元素),而用户将鼠标按住 button/span 我希望 requestAnimationFrame 完成它该死的工作!
而已!没问太多吧?
但据我所知,目前是该死的 requestAnimationFrame 导致了这个错误:
"Uncaught TypeError: Cannot read property 'id' of undefined"
最大的问题是:为什么???
为什么 requestAnimationFrame 说它是 "undefined"?
无需在此处使用 requestAnimationFrame 即可正常工作。
那么,请解释一下:
我到底做错了什么?
最重要的是:
为什么错了?
此代码:
window.requestAnimationFrame(moveEye);
...将导致 moveEye
被调用,有点像这样:
moveEye(timestamp);
请注意,没有传递任何事件,而是传递了一个时间戳。所以函数中的 e.target
将是未定义的,这就是 e.target.id
失败的原因。
如果您的目的是循环播放动画,则需要"capture" 相关数据。关闭将使这成为可能:
function moveEye(e) {
var btnClicked = e.target.id;
function animate() {
if (btnClicked === "right") {
eye.style.left = (eye.offsetLeft += 3) + "px";
} else if (btnClicked === "left") {
eye.style.left = (eye.offsetLeft -= 3) + "px";
} else if (btnClicked === "top") {
eye.style.top = (eye.offsetTop -= 3) + "px";
} else if (btnClicked === "bottom") {
eye.style.top = (eye.offsetTop += 3) + "px";
}
reqID = window.requestAnimationFrame(animate);
}
animate();
}
当调用 moveEye
以响应鼠标单击时,它会提供一个事件对象作为第一个参数。事件对象有一个 target
属性.
当 moveEye
作为 requestAnimationFrame
的回调被调用时,它被传递一个 DOMHighResTimeStamp 双精度数。 JavaScript 允许尝试通过自动将数字转换为数字对象来访问数字的属性。
但是,Number 对象没有 target
属性,因此 属性 查找返回的值为 undefined
。现在尝试访问未定义的 target
属性 的 id
属性 会生成错误。
更新
自然地,修复需要为单击事件处理程序和动画回调使用单独的函数。在处理完其他问题后,这里有一个移动#eyeball 的工作示例:
document.addEventListener("DOMContentLoaded", function() {
// parent div that holds the navigation wheel:
var navDiv = document.querySelector("#wrapper > section > div");
navDiv.addEventListener("mousedown", startEye, false);
navDiv.addEventListener("mouseup", stopEye, false);
var eye = document.querySelector("#eyeball"),
iris = document.querySelector("#iris"),
reqID,
btnClicked = "";
function startEye(e) {
btnClicked = e.target.id;
moveEye();
}
function moveEye() {
var left = parseFloat( eye.style.left);
var top = parseFloat( eye.style.top);
if ( btnClicked === "right") {
eye.style.left = (left + 3) + "px";
}
else if ( btnClicked === "left") {
eye.style.left = (left -3) + 'px';
}
else if ( btnClicked === "top") {
eye.style.top = (top - 3) + "px";
}
else if ( btnClicked === "bottom") {
eye.style.top = (top + 3) + "px";
}
reqID = window.requestAnimationFrame(moveEye);
}
function stopEye(e) {
cancelAnimationFrame(reqID);
}
},false);
<div id="wrapper">
<p>Description comes here.</p>
<section>
<p>Navigation Wheel:</p>
<div>
<span id="top">top</span>
<span id="bottom">bottom</span>
<span id="left">left</span>
<span id="right">right</span>
<span id="hub"></span>
</div>
</section>
<section id="stage">
<figure id="eyeball" style="position: relative; top:0px; left: 0px">
eyeball
<span id="shadow"></span>
<span id="iris" class="irisMove"></span>
</figure>
</section>
</div>
TLDR;
请注意,.offsetLeft
和 offsetTop
是只读元素属性。 (eye.offsetTop += 3)
或 (eye.offsetLeft -= 3)
等表达式不会更新 属性 的值,具有严重的误导性,不应保留在代码中。
CSS 属性 left
/top
和元素属性 offSetLeft
/offsetTop
不使用相同的参考原点。最初这导致单击 "left" 也向右移动元素。采用的解决方案是使用 CSS left
和 top
属性设置要移动的元素 (#eyeball) 的样式,并使用它们来读取和更新位置。这就是示例中未使用 offSetLeft
和 .offsetTop
的原因。
我是 JavaScript 初学者,我有一些代码在功能方面工作得很好,但它是如此 not DRY 和 "dirty"...光是看那段代码就让我恶心。 所以,我决定做一个非常漂亮、现代、干净和干燥的版本。 但是现在我完全被困住了,尽管到处搜索都找不到任何东西来理解我到底做错了什么? 最重要的是:为什么错了?
因此,这是当前代码:
document.addEventListener("DOMContentLoaded", function() {
// parent div that holds the navigation wheel:
var navDiv = document.querySelector("#wrapper > section > div");
navDiv.addEventListener("mousedown", moveEye, false);
navDiv.addEventListener("mouseup", stopEye, false);
var eye = document.querySelector("#eyeball"),
iris = document.querySelector("#iris"),
reqID;
function moveEye(e) {
var btnClicked = e.target.id;
// alert("Button clicked: " + btnClicked);
if (btnClicked === "right") {
eye.style.left = (eye.offsetLeft += 3) + "px";
} else if (btnClicked === "left") {
eye.style.left = (eye.offsetLeft -= 3) + "px";
} else if (btnClicked === "top") {
eye.style.top = (eye.offsetTop -= 3) + "px";
} else if (btnClicked === "bottom") {
eye.style.top = (eye.offsetTop += 3) + "px";
}
reqID = window.requestAnimationFrame(moveEye);
}
function stopEye(e) {
cancelAnimationFrame(reqID);
}
}, false);
<div id="wrapper">
<p>Description comes here.</p>
<section>
<p>Navigation Wheel:</p>
<div>
<span id="top"></span>
<span id="bottom"></span>
<span id="left"></span>
<span id="right"></span>
<span id="hub"></span>
</div>
</section>
<section id="stage">
<figure id="eyeball">
<span id="shadow"></span>
<span id="iris" class="irisMove"></span>
</figure>
</section>
</div>
请注意,我特别想保留这个:
var navDiv = document.querySelector("#wrapper > section > div");
因为我想尽可能保持代码干爽和干净。 这就是为什么我选择包含 4 个按钮的 parent div 而不是 div 中的 4 个按钮中的每一个。 所以,我想听父 div 告诉我当前正在单击里面的 4 个按钮中的哪一个(即 4 个跨度元素),而用户将鼠标按住 button/span 我希望 requestAnimationFrame 完成它该死的工作! 而已!没问太多吧?
但据我所知,目前是该死的 requestAnimationFrame 导致了这个错误: "Uncaught TypeError: Cannot read property 'id' of undefined"
最大的问题是:为什么??? 为什么 requestAnimationFrame 说它是 "undefined"? 无需在此处使用 requestAnimationFrame 即可正常工作。
那么,请解释一下: 我到底做错了什么? 最重要的是: 为什么错了?
此代码:
window.requestAnimationFrame(moveEye);
...将导致 moveEye
被调用,有点像这样:
moveEye(timestamp);
请注意,没有传递任何事件,而是传递了一个时间戳。所以函数中的 e.target
将是未定义的,这就是 e.target.id
失败的原因。
如果您的目的是循环播放动画,则需要"capture" 相关数据。关闭将使这成为可能:
function moveEye(e) {
var btnClicked = e.target.id;
function animate() {
if (btnClicked === "right") {
eye.style.left = (eye.offsetLeft += 3) + "px";
} else if (btnClicked === "left") {
eye.style.left = (eye.offsetLeft -= 3) + "px";
} else if (btnClicked === "top") {
eye.style.top = (eye.offsetTop -= 3) + "px";
} else if (btnClicked === "bottom") {
eye.style.top = (eye.offsetTop += 3) + "px";
}
reqID = window.requestAnimationFrame(animate);
}
animate();
}
当调用 moveEye
以响应鼠标单击时,它会提供一个事件对象作为第一个参数。事件对象有一个 target
属性.
当 moveEye
作为 requestAnimationFrame
的回调被调用时,它被传递一个 DOMHighResTimeStamp 双精度数。 JavaScript 允许尝试通过自动将数字转换为数字对象来访问数字的属性。
但是,Number 对象没有 target
属性,因此 属性 查找返回的值为 undefined
。现在尝试访问未定义的 target
属性 的 id
属性 会生成错误。
更新
自然地,修复需要为单击事件处理程序和动画回调使用单独的函数。在处理完其他问题后,这里有一个移动#eyeball 的工作示例:
document.addEventListener("DOMContentLoaded", function() {
// parent div that holds the navigation wheel:
var navDiv = document.querySelector("#wrapper > section > div");
navDiv.addEventListener("mousedown", startEye, false);
navDiv.addEventListener("mouseup", stopEye, false);
var eye = document.querySelector("#eyeball"),
iris = document.querySelector("#iris"),
reqID,
btnClicked = "";
function startEye(e) {
btnClicked = e.target.id;
moveEye();
}
function moveEye() {
var left = parseFloat( eye.style.left);
var top = parseFloat( eye.style.top);
if ( btnClicked === "right") {
eye.style.left = (left + 3) + "px";
}
else if ( btnClicked === "left") {
eye.style.left = (left -3) + 'px';
}
else if ( btnClicked === "top") {
eye.style.top = (top - 3) + "px";
}
else if ( btnClicked === "bottom") {
eye.style.top = (top + 3) + "px";
}
reqID = window.requestAnimationFrame(moveEye);
}
function stopEye(e) {
cancelAnimationFrame(reqID);
}
},false);
<div id="wrapper">
<p>Description comes here.</p>
<section>
<p>Navigation Wheel:</p>
<div>
<span id="top">top</span>
<span id="bottom">bottom</span>
<span id="left">left</span>
<span id="right">right</span>
<span id="hub"></span>
</div>
</section>
<section id="stage">
<figure id="eyeball" style="position: relative; top:0px; left: 0px">
eyeball
<span id="shadow"></span>
<span id="iris" class="irisMove"></span>
</figure>
</section>
</div>
请注意,.offsetLeft
和 offsetTop
是只读元素属性。 (eye.offsetTop += 3)
或 (eye.offsetLeft -= 3)
等表达式不会更新 属性 的值,具有严重的误导性,不应保留在代码中。
CSS 属性 left
/top
和元素属性 offSetLeft
/offsetTop
不使用相同的参考原点。最初这导致单击 "left" 也向右移动元素。采用的解决方案是使用 CSS left
和 top
属性设置要移动的元素 (#eyeball) 的样式,并使用它们来读取和更新位置。这就是示例中未使用 offSetLeft
和 .offsetTop
的原因。