首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >addEventListener和“`this`”不能按预期工作

addEventListener和“`this`”不能按预期工作
EN

Stack Overflow用户
提问于 2015-09-26 10:49:26
回答 3查看 1.2K关注 0票数 4

我是一个有经验的程序员,但对网络编程有点陌生。我正在试图学习Javascript,HTML5和SVG使用VS2010编写一个HTML页面,发挥Tic-Tac-脚趾与Javascript。

我正在成功地将九个方块中的每一个创建为SVG <rect...>元素,但是我在每个方块的单击事件处理程序上遇到了问题。

下面是HTML文件中存在的基本SVG元素:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
    id="svgTTT" width="150" height="150" viewBox="0 0 300 300"  >
    <rect width="3" height="300" x="99" fill="#008d46" />
    <rect width="3" height="300" x="199" fill="#000000" />
    <rect width="300" height="3" y="99" fill="#008d46" />
    <rect width="300" height="3" y="199" fill="#d2232c" />
  </svg>

这些静态<rect>元素绘制TicTacToe板的交叉散列行。九个板方块是在从windows事件调用的javascript函数中创建的(如下所示)。

下面是javascript (在HTML中的<script>元素中是行内的):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<script type="text/javascript">
  function getEl(s) { return document.getElementById(s); }

  var svg;  // get the TTT svg

  // execute after HTML has rendered
  window.onload = function () {
    svg = getEl("svgTTT");
    tttSetupSquares(svg);
    alert("click-squares are setup.");
  }

  var cells = new Array(3);
  // setup the click squares
  function tttSetupSquares(brd) {
    for (var i = 0; i < 3; i++) {
      cells[i] = new Array(3);
      for (var j = 0; j < 3; j++) {
        var x = 3 + (i * 100);
        var y = 3 + (j * 100);
        var newId = "svgTTTsqr" + i.toString() + j.toString();
        // add in new rect with html
        brd.innerHTML += "<rect id='"+newId+"' width='93' height='93' "
                        + "fill='#00DD00' x='" + x.toString() + "' y ='" + y.toString() + "'"
                        + " />";
        //find it using the newId
        var rect = document.getElementById(newId);
        //make a cell object to contain all of this
        var cell = {
          col: i,
          row: j,
          pSvg: svg,
          rect: rect,

          handleClick: function (event) {
            try {
              // this line fails because `this` is the target, not the cell
              var svgNS = this.pSvg.namespaceURI;

              var line = document.createElementNS(svgNS, 'line');
              this.pSvg.appendChild(line);
            }
            catch (err) {
              alert("handlClick err: " + err);
            }
          }
        }

        //add a click handler for the rect/cell
        cell.rect.addEventListener("click", cell.handleClick, false);
        //(only seems to work the last time it is executed)

        //save the cell object for later use
        cells[i][j] = cell;
      }
    }
  }
</script>

(我可以提供完整的页面源代码,但包含这些内容的只是HTML元素。)

问题有两方面:

  1. 只有最后一个addEventListener似乎起作用了。点击所有的其他方块什么也不做。单击最后一个方块(svgTTTsqr22)可以运行cell.handleClick,但会导致问题2(如下所示)。Chrome (F12)显示除最后一个以外的所有<rect>元素都没有事件侦听器。
  2. cell.handleClick运行时,它会在第一行(var svgNS = this.pSvg.namespaceURI;)上失败,出现一个错误,比如“未定义的对象没有一个名为"namespaceURI”的属性,在开发工具中检查显示它失败了,因为this不是设置为cell对象,而是设置为单击的SVG <rect>元素。

所以我的问题是:

答:我在这里做错了什么?

我怎么能这样做?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-09-26 15:05:39

1.缺少事件处理程序

使用innerHTML更改元素的内部结构将导致删除元素的所有子元素,并通过重新解析HTML内容来重建元素的DOM子树。通过删除子元素,所有以前注册的事件侦听器都会丢失,并且在从HTML重新构建DOM时不会自动恢复。为了避免这种行为,最好避免使用innerHTML,如果可能的话,使用直接的DOM操作。您可以使用这样的方法插入您的<rect>

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Use DOM manipulation instead of innerHTML
var rect = document.createElementNS(svg.namespaceURI, 'rect');
rect.setAttributeNS(null, "id", newId);
rect.setAttributeNS(null, "fill", "#00DD00");
rect.setAttributeNS(null, "width", "93");
rect.setAttributeNS(null, "height", "93");
rect.setAttributeNS(null, "x", x);
rect.setAttributeNS(null, "y", y);
svg.appendChild(rect);

事件处理程序内部的this 上下文

每当事件侦听器被调用时,this都会绑定到由事件触发的元素。但是,在您的代码中,您不需要this,因为所有信息都可以由参数brd获得,该参数被传递给函数.tttSetupSquares()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
handleClick: function (event) {
    try {
        var svgNS = brd.namespaceURI;
        var line = document.createElementNS(svgNS, 'line');
        brd.appendChild(line);
    }
    catch (err) {
        alert("handlClick err: " + err);
    }
}

有关工作示例,请参阅以下代码段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function getEl(s) { return document.getElementById(s); }

  var svg;  // get the TTT svg
  var cells = new Array(3);

  // execute after HTML has rendered
  !(function () {
    svg = getEl("svgTTT");
    tttSetupSquares(svg);
    alert("click-squares are setup.");
  }());

  // setup the click squares
  function tttSetupSquares(brd) {
    for (var i = 0; i < 3; i++) {
      cells[i] = new Array(3);
      for (var j = 0; j < 3; j++) {
        var x = 3 + (i * 100);
        var y = 3 + (j * 100);
        var newId = "svgTTTsqr" + i.toString() + j.toString();
        
        // Use DOM manipulation instead of innerHTML
		var rect = document.createElementNS(svg.namespaceURI, 'rect');
        rect.setAttributeNS(null, "id", newId);
        rect.setAttributeNS(null, "fill", "#00DD00");
        rect.setAttributeNS(null, "width", "93");
        rect.setAttributeNS(null, "height", "93");
        rect.setAttributeNS(null, "x", x);
        rect.setAttributeNS(null, "y", y);
        svg.appendChild(rect);
        
        //make a cell object to contain all of this
        var cell = {
          col: i,
          row: j,
          pSvg: brd,
          rect: rect,

          handleClick: function (event) {
            try {
              //console.log(this);
              var svgNS = brd.namespaceURI;
              var line = document.createElementNS(svgNS, 'line');
              line.setAttributeNS(null, "x1", this.x.baseVal.value);
              line.setAttributeNS(null, "y1", this.y.baseVal.value);
              line.setAttributeNS(null, "x2", this.x.baseVal.value + this.width.baseVal.value);
              line.setAttributeNS(null, "y2", this.y.baseVal.value + this.height.baseVal.value);
              brd.appendChild(line);
            }
            catch (err) {
              alert("handlClick err: " + err);
            }
          }
        }

        //add a click handler for the rect/cell
        cell.rect.addEventListener("click", cell.handleClick, false);

        //save the cell object for later use
        cells[i][j] = cell;
      }
    }
  }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
line {
  stroke: red;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
    id="svgTTT" width="150" height="150" viewBox="0 0 300 300"  >
    <rect width="3" height="300" x="99" fill="#008d46" />
    <rect width="3" height="300" x="199" fill="#000000" />
    <rect width="300" height="3" y="99" fill="#008d46" />
    <rect width="300" height="3" y="199" fill="#d2232c" />
  </svg>

票数 4
EN

Stack Overflow用户

发布于 2015-09-26 13:25:43

正如上面所解释的,“这个”的问题在于它被绑定到一个上下文,而这个上下文并不总是您想要的上下文。这类问题有很多解决办法。从臭名昭著的self=this戏法到.bind()。对此也有许多答案,一般来说,可能会有重复的答案。一个很好的答案和一些后续阅读可以在这里找到:

var self = this?

或者在这里:How to change the context of a function in javascript

或者在这里:http://ryanmorr.com/understanding-scope-and-context-in-javascript/

或者在这里:https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Operators/this

虽然,但您的问题的真正答案是非常具体的。在事件处理程序的情况下,有一个"this"-problem的解决方案。您只需实现一个EventListener接口即可。这听起来比现在复杂多了。事实上,这很容易。您的对象只需实现一个函数:.handleEvent。当您将一个对象传递给addEventListener()函数时,这个函数会自动调用。这方面的好处是,使用这种方法," this“的上下文将自动正确。不需要黑客或解决办法。当然,知道一般情况的解决方案是很好的,但是对于这种特殊情况,.handleEvent 是解决方案。

下面是一个完整的工作示例:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  function getEl(s) { return document.getElementById(s); }

  var svg;  // get the TTT svg

  // execute after HTML has rendered
  window.onload = function () {
    svg = getEl("svgTTT");
    tttSetupSquares(svg);
    //alert("click-squares are setup.");
  }

  var cells = new Array(3);
  // setup the click squares
  function tttSetupSquares(brd) {
    for (var i = 0; i < 3; i++) {
      cells[i] = new Array(3);
      for (var j = 0; j < 3; j++) {
        var x = 3 + (i * 100);
        var y = 3 + (j * 100);
        var rect= document.createElementNS("http://www.w3.org/2000/svg","rect")
rect.setAttribute("x",x);
rect.setAttribute("y",y);
rect.setAttribute("width",100);
rect.setAttribute("height",100);
rect.setAttribute("fill","grey")

        brd.appendChild(rect)
        var cell = {
          col: i,
          row: j,
          pSvg: svg,
          rect: rect,

          handleEvent: function (event) {
            try {
              // this line fails because `this` is the target, not the cell
              var svgNS = this.pSvg.namespaceURI;

              var line = document.createElementNS(svgNS, 'line');
              line.setAttribute("x1",this.rect.getAttribute("x"))
              line.setAttribute("y1",this.rect.getAttribute("y"))
              line.setAttribute("x2",this.rect.getAttribute("x")*1+this.rect.getAttribute("width")*1)
              line.setAttribute("y2",this.rect.getAttribute("y")*1+this.rect.getAttribute("height")*1)
              line.setAttribute("stroke","red")
              this.pSvg.appendChild(line);
              document.getElementById("out").innerHTML="rect("+this.col+","+this.row+") was clicked"
            }
            catch (err) {
              alert("handlClick err: " + err);
            }
          }
        }

        //add a click handler for the rect/cell
        cell.rect.addEventListener("click", cell, false);
        //(only seems to work the last time it is executed)

        //save the cell object for later use
        cells[i][j] = cell;
      }
    }
  }
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
    id="svgTTT" width="150" height="150" viewBox="0 0 300 300"  >
    <rect width="3" height="300" x="99" fill="#008d46" />
    <rect width="3" height="300" x="199" fill="#000000" />
    <rect width="300" height="3" y="99" fill="#008d46" />
    <rect width="300" height="3" y="199" fill="#d2232c" />
  </svg>

<div id="out"></div>

在EventHandlers的情况下,这是正确的解决方案,其他一切都是黑客!

票数 3
EN

Stack Overflow用户

发布于 2015-09-26 11:09:49

一些建议:

您可以研究如何使用事件委托。如果您使用像jQuery这样的框架或库,或者使用角度或响应,它将自动为您执行事件委托。在单独的DOM元素上设置许多事件处理程序可能会影响性能。相反,您可以做的是在包装元素上设置一个“单击”处理程序,并使用event.target属性查看实际单击的元素。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
svg.addEventListener("click", function (e) {
    if (e.target.nodeName === "rect" && (/^svgTTTsqr/).test(e.target.id)) {
        // Use a regexp on  e.target.id to find your
        // cell object in `cells`
    }
});

regexp可能有点脏,所以您应该使用数据属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Generating the HTML
brd.innerHTML += "<rect id='"+newId+"' data-i='" + i + "' data-j='" + j + "' "

// The event handler:
svg.addEventListener("click", function (e) {
    if (e.target.nodeName === "rect" && (/^svgTTTsqr/).test(e.target.id)) {
        var i = e.target.getAttribute("data-i");
        var j = e.target.getAttribute("data-j");
        var cell = cells[i][j];
        cell.handleClick();
    }
});

如果这样做,还可以轻松地进行另一个性能调整,即首先生成整个HTML字符串,并在一个操作中将其附加到DOM中,因为不再需要在循环时将HTML插入DOM并添加事件侦听器。

至于你的问题,

1)对不起,不能帮您:(需要设置一个可执行的示例并四处查看,在读取代码时什么都想不出来。无论如何,我将发布这个答案,因为希望上面解释的事件委托解决方案能够解决问题。

( 2)被称为“原始函数”的原始函数将其“此”绑定到其调用的任何范围。调用方还可以显式设置“this”绑定到的内容。解决方案是创建一个被包装的新函数,这样‘这’就会被迫成为你想要它成为的样子。使用内置的function () {}.bind(cell),它将返回一个包装原始函数的新函数,而在原始this中,不管bind返回的函数是什么上下文,总是将其设置为cell

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32800782

复制
相关文章
attachEvent和addEventListener区别
target.addEventListener(type, listener, useCapture);
书童小二
2018/09/03
9090
addEventListener() 方法
提示: 使用 removeEventListener() 方法来移除 addEventListener() 方法添加的事件句柄。
全栈程序员站长
2022/09/14
9580
在元素上写事件和addEventListener()的区别[通俗易懂]
语法: element.addEventListener( type , listener , useCapture ) – – 添加事件监听 – – type: 事件类型字符串,不使用“on”前缀 – – callback:事件处理程序(回调函数) – – useCapture:可选参数,是否使用事件捕获的方式处理事件。不传递时,默认为false,表示不使用事件捕获(使用事件冒泡),如果需要显示事件捕获,则显示传递true。
全栈程序员站长
2022/09/18
1.2K0
兼容addEventListener事件
addEventListener绑定事件,但是只在ie 9以上版本才有用,以下版本不可用,ie9以下版本用attachEvent: 1)addEventListener有三个参数,第一个为type,代表事件名称,第二个为handler,为事件处理甘薯,第三个为capture,boolean值。fasle代表冒泡。事件从下往上触发,true代表捕获,事件从上往下触发。 2)attachEvent只有两个参数,第一个为“on”+type,即第一点中addEventListener第一个参数为click,则attachEvent的参数为onclick加上on,第二个参数为handler,事件处理函数。
全栈程序员站长
2022/11/04
1.5K0
addEventListener() 方法,事件监听[通俗易懂]
注意:不要使用 “on” 前缀。 例如,使用 “click” ,而不是使用 “onclick”。
全栈程序员站长
2022/09/14
2.8K0
addEventListener() 方法,事件监听[通俗易懂]
onclick与addEventListener区别
这次做项目遇到了这个问题,本来习惯性的每次都写的是addEventListener绑定click事件。但是当用addEventListener绑定了多次click事件的时候,引发了我的思考,这两者有区别吗?具体的事件分析可查看另一篇文章
全栈程序员站长
2022/09/14
1.5K0
[转]addEventListener() 方法,事件监听
转载  白杨-M  http://www.cnblogs.com/baiyangyuanzi/p/6627401.html addEventListener() 方法,事件监听 你可以使用 removeEventListener() 方法来移除事件的监听。 语法 element.addEventListener(event, function, useCapture); 第一个参数是事件的类型 (如 "click" 或 "mousedown"). 第二个参数是事件触发后调用的函数。 第三个参数是个布尔值用于
悟空聊架构
2018/05/18
2.1K0
js添加事件和移除事件:addEventListener()与removeEventListener()
addEventListener()与removeEventListener()用于处理指定和删除事件处理程序操作。
全栈程序员站长
2022/09/14
8.6K0
js添加事件和移除事件:addEventListener()与removeEventListener()
移动端(video) transformOringe addEventListener
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style type="text/css"> *{margin: 0;padding: 0;list-style: none;} div{width: 200px;height: 2
贵哥的编程之路
2020/10/28
5720
TestNG忽略测试和预期异常
一.有时候我们写的代码还没有构思完整, 但测试case又需要执行,testng提供了@Test (Enable=false) ,可以有助于禁用.
louiezhou001
2020/02/25
8830
window.onerror 和window.addEventListener('error')的区别
1. 定义window.onerror全局事件函数 window.onerror = function(message, source, lineno, colno, error) { ... } / * * message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。 * source:发生错误的脚本URL(字符串) * lineno:发生错误的行号(数字) * colno:发生错误的列号(数字) * error:Error对象 */ 是一个全局变
蓓蕾心晴
2022/12/30
4.1K0
window.onerror 和window.addEventListener('error')的区别
Javascript 的addEventListener()及attachEvent()区别分析
Mozilla中:  addEventListener的使用方式:  target.addEventListener(type, listener, useCapture);  target: 文档节点、document、window 或 XMLHttpRequest。  type: 字符串,事件名称,不含“on”,比如“click”、“mouseover”、“keydown”等。  listener :实现了 EventListener 接口或者是 JavaScript 中的函数。  useCapture
smy
2018/04/03
1.5K0
企业做网站要有合理的预期和预算
在经历了多家企业网站制作的过程后,美耐思发现如果企业主一心只想着怎么降低网站制作费用做成自己满意的网站时,往往事与愿违,钱是花少了,可是网站却做的较糟糕。无论是从SEO角度,还是美工角度,程序方面都有不少的瑕疵。完了之后由于价格较低,网站制作方随后也不想再做太多完善,最终可能大家都不满意,不欢而散,企业本身并没有占到什么便宜,钱是花少了,可是网站并没有做好。
天津做网站-美耐思
2018/10/27
4790
业绩超预期因子
学术界很早就发现,股票市场存在显著的盈余公告后的价格偏移现象(Post-Earnings Announcement Drift PEAD)。通俗解释来说,投资者对于公司的盈利有一个预期值,如果财报公布后,公司的实际盈利超出了投资者预期,公司的股价会上升,会有明显的超额收益。如果实际盈利低于投资者预期,公司股价会下降,会有明显的负向收益。本文基于这一现象构造盈利超预期因子,并对因子进行测试。后台回复“业绩超预期”获取代码和参考文献,限时免费。
量化小白
2020/03/18
2.9K0
业绩超预期因子
js-addEventListener()第三个参数useCapture
第3个参数叫做useCapture,是一个boolean值,就是true or false 。如果送出true的话就是浏览器会使用Capture方式,false的话是Bubbling,只有在特定状况下才会有影响,通常建议是false,而会有影响的情形是目标元素(target element)有祖先元素(ancestor element),而且也有同样的事件对应函数
acoolgiser
2020/11/03
1.1K0
美光警告:裁员人数将高于预期,本季毛利率可能低于预期!
3月4日消息,据多家外媒报道,存储芯片大厂美光(Micron)警告称,受到产业供给过剩影响,公司裁员人数恐怕会比先前披露的还多,第三季(3-5 月)毛利率也很可能逊于高层原本预期。
芯智讯
2023/03/24
2230
美光警告:裁员人数将高于预期,本季毛利率可能低于预期!
【董天一】Filecoin: 影响力容错(PFT)和预期共识(EC)
这是filecoin协议里面对 PFT的解释,power概念就是矿工的影响力(influence),“Power”是filecoin系统的投票权力的大小度量,根据矿工贡献的Power来计算矿工的投票权有多大,根据信达雅的基本要求,所以称为“影响力容错”。之前曾经考虑过“权利容错”和“权益容错”,“权利”这个词本身表达不了大小的概念,而“权益”又跟Proof of Stake(权益证明:EC共识是在这个基础上进行的,有必要区分)有冲突,于是小编把这个词翻译成影响力容错。或许有更贴切的表达方式,欢迎讨论!
圆方圆学院
2018/11/12
8470
【董天一】Filecoin: 影响力容错(PFT)和预期共识(EC)
点击加载更多

相似问题

addEventListener和wmp未按预期工作

12

和getClass()不能按预期工作

27

显示()和切换()不能按预期工作

110

setTimeout()和remove()不能按预期工作

26

memcpy()和memmove()不能按预期工作

15
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文