为什么requestAnimationFrame生成" Uncaught TypeError:无法读取属性' id'未定义"?

时间:2017-09-25 22:08:59

标签: javascript requestanimationframe

我是JavaScript初学者,我有一些代码在功能方面完美运行但是 DRY和"脏"。 ..只是看着那段代码让我感到恶心。 所以,我决定做一个非常漂亮,现代,干净和干燥的版本。 但是现在我完全陷入了困境,尽管搜索高低也无法找到任何可以理解的东西我做错了什么? 最重要的是:为什么这是错的?

所以,这是当前的代码:



        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>
&#13;
&#13;
&#13;

请注意,我特别想保持这个:

var navDiv = document.querySelector("#wrapper > section > div");

因为我希望将代码保持为DRY并且尽可能保持干净。 这就是为什么我选择包含4个按钮的 div的原因 而不是单独的4个按钮中的每一个。 所以,我想听父div来告诉我当前正在点击里面的4个按钮(即4个span元素)中的哪一个,当用户按住鼠标按下按钮/ span时我希望requestAnimationFrame为做它的工作! 那就是它!没问题,对吧?

但正如我所理解的那样,导致此错误的darn requestAnimationFrame: &#34;未捕获的TypeError:无法读取属性&#39; id&#39;未定义&#34;

最大的问题是:为什么??? 为什么requestAnimationFrame说它&#34;未定义&#34; ?? 它工作正常,而不会在这里使用requestAnimationFrame。

所以,请解释一下: 我究竟做错了什么? 最重要的是: 为什么这是错的?

2 个答案:

答案 0 :(得分:1)

当响应鼠标单击调用moveEye时,会为其提供一个事件对象作为第一个参数。事件对象具有target属性。

moveEye调用requestAnimationFrame作为回调时,会传递DOMHighResTimeStamp双精度数。 JavaScript允许尝试通过自动将数字转换为Number对象来访问数字上的属性。

但是,Number对象没有target属性,因此从属性查找返回的值为undefined。现在,尝试访问未定义的id属性的target属性会生成错误。

<小时/> 的更新
当然,修复需要为click事件处理程序和动画回调使用单独的函数。在讨论了其他问题后,这是一个移动#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;

请注意,.offsetLeftoffsetTop是只读元素属性。像(eye.offsetTop += 3)(eye.offsetLeft -= 3)这样的表达式不会更新属性的值,会严重误导,不应该留在代码中。

CSS属性left / top和元素属性offSetLeft / offsetTop不使用相同的参考来源。最初这导致单击“左”以向右移动元素。采用的解决方案是使用CSS lefttop属性设置要移动的元素(#eyeball)的样式,并使用它们来读取和更新位置。这就是示例中未使用offSetLeft.offsetTop的原因。

答案 1 :(得分:0)

此代码:

window.requestAnimationFrame(moveEye);

...将导致moveEye被调用,有点像这样:

moveEye(timestamp);

请注意,没有传递任何事件,而是传递时间戳。因此函数中的e.target将是未定义的,这就是e.target.id失败的原因。

如果你的目的是做一个动画循环,你需要&#34;捕捉&#34;相关数据。关闭会使这成为可能:

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();
}