AngularJS DOM修改删除事件->需要可行的变通方法实现模式

AngularJS DOM modification deletes events -> need viable workaround implementation schema

本文关键字:模式 实现 方法 删除 修改 DOM 事件 gt AngularJS      更新时间:2023-09-26

有人能向我解释一下在AngularJS中创建抵制DOM修改的自定义事件侦听器的正确方法吗?

我的问题是,我需要根据下拉菜单绘制2个不同的数据集。数据集的每个元素都必须响应一个事件(mouseover/mousemove等)。但当从一个数据集切换到另一个数据集中时,事件侦听器会丢失。这种行为在这里得到了很好的解释:

  • 将HTML注入DOM会破坏AngularJS事件

但我不明白绕过它的答案。帖子提到手动避开ng应用程序可能会有所帮助,但这似乎是一个糟糕的解决方法。

我宁愿重新定义我自己的事件监听器,这样它们就不会在DOM修改过程中丢失。有人能告诉我如何在指令/控制器模型中正确使用addEventListener吗?

这里有一个SSCCE html/javascript文件,它说明了我的程序是如何组织的

angular.module("testUpdate", [])
.directive("shape", function($parse) {
  return {
    restrict: "E",
    replace: false,
    scope: false,
    link: function(scope, element, attrs) {
      
      // draw dataset according to #shapeSelection choice
      d3.select("#shapeSelection")
        .on("change", scope.redraw);
      
      // change color on mouseover
      d3.selectAll("circle,rect,polygon")
        .on("mouseover", function() {
          d3.select(this).style("fill", "#0f0");
        });
    }
  };
})
.controller("testController", function($scope) {
  $scope.shape = "none";
  
  //create datasets
  
  $scope.dataset1 = [{
    cx: 10,
    cy: 20
  }, {
    cx: 30,
    cy: 20
  }];
  $scope.dataset2 = [{
    w: 20,
    h: 20
  }];
  // the triangle actually responds to the event
  
  d3.selectAll("svg")
    .append("polygon")
    .attr("points", "15,20 0,40 30,40")
    .style("fill", "#f00");
  
  // draw dataset depending on the user choice
  $scope.draw = function() {
    if ($scope.shape === "circle") {
      d3.selectAll("svg").selectAll("circle")
        .data($scope.dataset1).enter()
        .append("circle")
        .attr("cx", function(d) { return d.cx; })
        .attr("cy", function(d) { return d.cy; })
        .attr("r", 10)
        .style("fill", "#f00");
    } 
    
    else if ($scope.shape === "rect") {
      d3.selectAll("svg").selectAll("rect")
        .data($scope.dataset2).enter()
        .append("rect")
        .attr("width", function(d) { return d.w; })
        .attr("height", function(d) { return d.h; })
        .attr("x", 0)
        .attr("y", 10)
        .style("fill", "#f00");
    } 
    
    else {}
  }
  
  // once removed is called, the event listener is destroyed
  $scope.redraw = function() {
    d3.selectAll("circle,rect,polygon").remove();
    $scope.draw()
  }
});
<script src="https://code.angularjs.org/1.2.15/angular.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<div ng-app="testUpdate" ng-controller="testController">
  <select name="shapeSelector" ng-model="shape" id="shapeSelection">
    <option value="none">--none--</option>
    <option value="circle">dataset1</option>
    <option value="rect">dataset2</option>
  </select>
  <shape id="shapeTag">
    <svg id="shapeSVG" display="block"></svg>
  </shape>
</div>

d3.selection.remove()函数修改DOM并使事件侦听器无效,以便三角形正确响应,而不是圆形和矩形。

一个糟糕的解决方法是为两个数据集创建DOM,并将未选择的数据集的可见性设置为隐藏,但两个数据集中都很大,因此这不是一个可行的选项。

提前感谢

从代码中,您似乎只想在shape指令内的元素上捕获mouseover事件。

我认为对您来说,最好的解决方案是将事件处理程序与元素绑定的代码移动到$scope.redraw$scope.draw函数中:

  $scope.draw = function() {
    if ($scope.shape === "circle") { 
       /* ... */ 
    } else {}

    // change color on mouseover
    d3.selectAll("circle,rect,polygon")
      .on("mouseover", function() {
        d3.select(this).style("fill", "#0f0");
      });
  }

当您在D3land中操作DOM时,不应该依赖Angular的链接函数中定义的事件处理程序。在我编写的指令中,当数据更新时,我只在指令中使用$watch来触发redraw函数,就像使用change函数一样。


替代解决方案

另一方面,如果您想处理指令中的事件(例如,如果您不必将d3传递给$scope),如果您使用的是jQuery,则可以在具有正确selectors的父级上使用.on方法。请注意,jQlite(Angular的jQuery内部实现)并不假定.on具有selectors

在这种情况下,即使您更改了下面的DOM,也会在父级调用事件处理程序。如果希望访问与事件处理程序内的节点关联的data,可以使用var data = d3.select(this).data()