【问题标题】:For loop assigning only the last value of an array to eventlistenersFor循环仅将数组的最后一个值分配给事件监听器
【发布时间】:2017-10-16 14:36:37
【问题描述】:

我有以下 AngularJS 控制器;

app.controller('playerController', ['$scope', '$http', 'playerService', function ($scope, $http, playerService) {
     $scope.soundeffects = playerService.getSoundeffects();

     function soundeffectCreation() {
        var maxrows = 3;
        var n = 0;

        var buttonTable = "<table class='table text-center'><tr>";

        for (var item = 0; item < $scope.soundeffects.length; item++) {
            var soundeffect = $scope.soundeffects[item];
            var soundeffectName = soundeffect.name;
            buttonTable += "<td><label>" + soundeffectName + "</label><button type='button' class='btn btn-default btn-block' id='" + soundeffectName + "'><i class='fa fa-play'></i></button></td>"
             if(n == maxrows) {
                buttonTable += "</tr><tr>"
                n = 0;
             }
             else{
                n++;
            }
        }
        buttonTable += "</tr></table>";
        document.getElementById('soundeffects').innerHTML = buttonTable;

         for (var item = 0; item < $scope.soundeffects.length; item++) {
             var soundeffect = $scope.soundeffects[item];
             var soundeffectName = soundeffect.name;
             var button = document.getElementById(soundeffectName);

             console.log(soundeffect);
             console.log(soundeffectName);
             console.log(button);
             button.addEventListener('click', function () {
                console.log(soundeffectName);
            })
        }
    }

    soundeffectCreation();
}

它在以下 HTML 中发挥了魔力:

<div id="soundeffects"></div>

使用目前仅包含 3 个项目的 JSON 数组:

{
   "soundeffects": [
      {
        "name": "Wilhelm Scream",
        "video": "r6JK-gRELI0"
      },
      {
        "name": "Fireball",
        "video": "AHRf27GPhQc"
      },
      {
        "name": "Mario Jump",
        "video": "37-paiEz0mQ"
      }
   ]
}

在 playerService 中成功检索到此 JSON。 表中的按钮创建得很好,它们获得了各自的标签,并且正确分配了 id,问题出现在第二个 for 循环中。 (有第二个 for 循环的原因是因为我无法在实际创建表之前添加事件监听器)。

addEventListener 之外的前三个 console.log 返回正确的值:Console

三个不同的对象及其各自的名称和视频。 三个不同的名称(Wilhelm Scream、Fireball 和 Mario Jump)。 三个不同的按钮,其 id 与上述名称相同。

但是,当我单击创建的按钮时,每个按钮都会返回数组的最后一个值“Mario Jump”,而不是相应元素的名称。尽管按钮的 id 是正确的,但一切都是如此。有什么建议我哪里出错了吗?


替代

作为替代方案,我尝试将ng-click='$scope.playSoundeffect(" + soundeffect + ")' 放在原始按钮创建器中,但这会将音效放在[Object object] 中。也欢迎任何有关如何使该方法发挥作用的建议。


为后代编辑

在获得更多 AngularJS 经验后,我重新制作了整个部分。我删除了整个soundeffectCreation() 函数并用以下html替换它:

<div ng-repeat="soundeffect in soundeffects track by $index" class="col-xs-6 col-md-4 col-lg-3">
    <div class="text-nowrap"><label>{{soundeffect.name}}</label></div>
    <div>
        <button class="btn btn-default" ng-click="playSoundeffect(soundeffect)">
            <i class="fa fa-play"></i>
        </button>
    </div>
</div>

【问题讨论】:

  • ID 中不能有空格
  • 记住var 不是块作用域。使用let 或在循环内创建一个闭包。

标签: javascript angularjs arrays json for-loop


【解决方案1】:

在 for 循环中使用 let 而不是 var

for (let item = 0; item < $scope.soundeffects.length; item++) {
     let soundeffect = $scope.soundeffects[item];
     let soundeffectName = soundeffect.name;
     let button = document.getElementById(soundeffectName);

     console.log(soundeffect);
     console.log(soundeffectName);
     console.log(button);
     button.addEventListener('click', function () {
        console.log(soundeffectName);
    })
}

问题是每个循环迭代都没有明显的闭包 - 请查看此 SO answer 以获取关于 for 循环中闭包的详细说明。

不支持let的浏览器解决方案

您还可以使用自调用匿名函数为每次迭代创建新范围:

for (var item = 0; item < $scope.soundeffects.length; item++) {
     (function(i) {
         var soundeffect = $scope.soundeffects[i];
         var soundeffectName = soundeffect.name;
         var button = document.getElementById(soundeffectName);

         console.log(soundeffect);
         console.log(soundeffectName);
         console.log(button);
         button.addEventListener('click', function () {
            console.log(soundeffectName);
        })
     })(item);
}

【讨论】:

  • 很好的解决方案,但检查caniuse.com/#feat=let,浏览器支持不是 100%。所以取决于浏览器的支持要求。
  • 答案已更新为不支持let的旧浏览器的解决方案
  • 浏览器支持要求最低。这主要是为我和一些保持浏览器最新的技术头脑的朋友准备的,所以两者都适合我。非常感谢。
【解决方案2】:

你需要像这样在闭包中捕获soundeffectName

button.addEventListener('click', function (soundeffectName) {
    console.log(soundeffectName);
}.bind(null, soundeffectName))

【讨论】:

    【解决方案3】:

    你正在处理闭包

     var soundeffectName = soundeffect.name;
    
     button.addEventListener('click', function () {
         console.log(soundeffectName);
     })
    

    soundEffect 是外部作用域的变量,所有的监听器都会持有对外部作用域的引用,它们都会指向内存中相同的位置,即最后一次迭代的值。要将值的副本保留在侦听器中,请尝试将代码包装在 IIFE 中

    (function(name){
      button.addEventListener('click', function () {
       console.log(name);
      })
    })(soundeffectName)
    

    【讨论】:

      猜你喜欢
      • 2019-07-07
      • 1970-01-01
      • 1970-01-01
      • 2021-03-17
      • 2023-03-19
      • 1970-01-01
      • 1970-01-01
      • 2023-02-08
      • 1970-01-01
      相关资源
      最近更新 更多