如何通过匿名函数分配鼠标悬停时传递局部变量?

时间:2021-07-04 23:53:48

I have a setup function that runs onload to add some behaviours to elements. The setup function passes arguments to the mouseover event but those arguments are being changed during the for loop because they are local references.

我有一个运行onload的设置函数,以向元素添加一些行为。 setup函数将参数传递给mouseover事件,但这些参数在for循环期间被更改,因为它们是本地引用。

function setupAreas( image, map, lots ) {
    // obj is a simple wrapper for doc.getElementById
    var image = obj(image); // image for imagemap
    var map = obj(map); // imagemap element

    var areas = map.getElementsByTagName('area');
    for (var i in areas) {
        var area = areas[i]; // imagemap area element
        area.id = area.alt;
    }

    for (var lot_id in lots) {
        if (lot_id != 'Lot No' && lot_id != '') {
            var area = document.getElementById(lot_id);
            if (!area || !area.coords) {
                alert('no map coords for lot '+lot_id);
            } else {
                var coords = area.coords.split(",");
                //alert('tag: '+area.tagName+' id: '+lot_id+' area: '+area);
                var details = lots[lot_id];
                if (details) {
                    // setup mouseover call with complete details of area
                    area.onmouseover = function(){ showLot(lot_id, area, coords, details, image, map, areas, lots) };
... snip ...

The problem is that because of the for loop the references lot_id and area are changed on each iteration. The result is the mouseover event for any element gives the lot_id and area of the last area only.

问题是因为for循环,每次迭代都会更改引用lot_id和area。结果是任何元素的mouseover事件只给出了最后一个区域的lot_id和区域。

I don't want or need jQuery for this. A simple JS solution that doesn't pollute the global namespace is preferred.

我不想或不需要jQuery。一个不污染全局命名空间的简单JS解决方案是首选。

3 个解决方案

#1


Try surrounding the contents of your for loop in a closure:

尝试在闭包中包含for循环的内容:

for (var lot_id in lots) {
    (function(lid){
        //contents of for loop - use lid instead of lot_id    
    })(lot_id);
}

let me know how that works out

让我知道这是如何工作的

EDIT: You don't have to surround the whole loop actually, you could just surround the line that attaches the event:

编辑:你不必实际包围整个循环,你可以只包围附加事件的行:

(function(lid){
    area.onmouseover = function(){ showLot(lid, area, coords, details, image, map, areas, lots) };
})(lot_id);

However surrounding the whole loop may prevent future bugs arising :)

然而,围绕整个循环可能会防止未来的错误:)

#2


You need to create a closure around your function. Something like this might help:

您需要在函数周围创建一个闭包。这样的事情可能会有所帮助:

function makeShowLot(lot_id, area, coords, details, image, map, areas, lots) {
  return function () { 
      showLot(lot_id, area, coords, details, image, map, areas, lots);
    };
}

Then, do this instead:

然后,改为:

area.onmouseover = makeShowLot(lot_id, area, coords, details, image, map, areas, lots);

makeShowLot is a function that returns a function. That function that is returned takes no arguments; all of the arguments needed for showLot are enclosed in this anonymous function.

makeShowLot是一个返回函数的函数。返回的函数不带参数; showLot所需的所有参数都包含在此匿名函数中。

#3


As you correctly observed because of closure, 'lot_id' is captured and it's the same for all mouseover events. Fixing the problem is simple, before you assign onmouseover, store the lot_id in another local var, say lotIdForMouseOver, and pass that to the mouseover function. The new local var thing will work in C#, not in JavaScript. At work, I do lot of C# and hence the confusion!

正如你因为关闭而正确观察到的那样,'lot_id'被捕获并且对于所有鼠标悬停事件都是相同的。修复问题很简单,在分配onmouseover之前,将lot_id存储在另一个本地var中,比如lotIdForMouseOver,并将其传递给mouseover函数。新的本地var将在C#中工作,而不是在JavaScript中。在工作中,我做了很多C#,因此混乱!

Like pkaeding suggested, create a helper function and you should be good.

像pkaeding建议的那样,创建一个辅助函数,你应该很好。

On a side note, if you 'inverse' your 'if' checks, you can get rid of the nested ifs. IMHO, nested if's are very difficult to follow.

另外,如果你'反''你的'if'检查,你可以摆脱嵌套的ifs。恕我直言,嵌套如果很难遵循。

Here's how I would do it.

我就是这样做的。

function setupAreas(image, map, lots)
{
    // existing code

    for(var lot_id in lots)
    {
        if(lot_id == 'Lot No' || lot_id == '')
            continue;

        var area = document.getElementById(lot_id);

        if(!area || ! area.coords)
        {
            alert('no maps for coords for lot ' + lot_id);
            continue;
        }

        var coords = area.coords.split(",");
        var details = lots[lot_id];

        if(! details)
            continue;

        //makeMouseOver function takes 'n' arguments and returns a function which
        //will call showLot with those same 'n' arguments.

        //This is the same suggestion as pkaeding, only that I have exploited 'arguments'
        //property to make it simpler. 
        var makeMouseOver = function()
        {
            var creationArgs = arguments;
            return function() { showLot.apply(null, creationArgs); };
        }

        area.onmouseover = makeMouseOver(lot_id, area, coords, details, image, map, area, lots);

        // more code.
    }
}

#1


Try surrounding the contents of your for loop in a closure:

尝试在闭包中包含for循环的内容:

for (var lot_id in lots) {
    (function(lid){
        //contents of for loop - use lid instead of lot_id    
    })(lot_id);
}

let me know how that works out

让我知道这是如何工作的

EDIT: You don't have to surround the whole loop actually, you could just surround the line that attaches the event:

编辑:你不必实际包围整个循环,你可以只包围附加事件的行:

(function(lid){
    area.onmouseover = function(){ showLot(lid, area, coords, details, image, map, areas, lots) };
})(lot_id);

However surrounding the whole loop may prevent future bugs arising :)

然而,围绕整个循环可能会防止未来的错误:)

#2


You need to create a closure around your function. Something like this might help:

您需要在函数周围创建一个闭包。这样的事情可能会有所帮助:

function makeShowLot(lot_id, area, coords, details, image, map, areas, lots) {
  return function () { 
      showLot(lot_id, area, coords, details, image, map, areas, lots);
    };
}

Then, do this instead:

然后,改为:

area.onmouseover = makeShowLot(lot_id, area, coords, details, image, map, areas, lots);

makeShowLot is a function that returns a function. That function that is returned takes no arguments; all of the arguments needed for showLot are enclosed in this anonymous function.

makeShowLot是一个返回函数的函数。返回的函数不带参数; showLot所需的所有参数都包含在此匿名函数中。

#3


As you correctly observed because of closure, 'lot_id' is captured and it's the same for all mouseover events. Fixing the problem is simple, before you assign onmouseover, store the lot_id in another local var, say lotIdForMouseOver, and pass that to the mouseover function. The new local var thing will work in C#, not in JavaScript. At work, I do lot of C# and hence the confusion!

正如你因为关闭而正确观察到的那样,'lot_id'被捕获并且对于所有鼠标悬停事件都是相同的。修复问题很简单,在分配onmouseover之前,将lot_id存储在另一个本地var中,比如lotIdForMouseOver,并将其传递给mouseover函数。新的本地var将在C#中工作,而不是在JavaScript中。在工作中,我做了很多C#,因此混乱!

Like pkaeding suggested, create a helper function and you should be good.

像pkaeding建议的那样,创建一个辅助函数,你应该很好。

On a side note, if you 'inverse' your 'if' checks, you can get rid of the nested ifs. IMHO, nested if's are very difficult to follow.

另外,如果你'反''你的'if'检查,你可以摆脱嵌套的ifs。恕我直言,嵌套如果很难遵循。

Here's how I would do it.

我就是这样做的。

function setupAreas(image, map, lots)
{
    // existing code

    for(var lot_id in lots)
    {
        if(lot_id == 'Lot No' || lot_id == '')
            continue;

        var area = document.getElementById(lot_id);

        if(!area || ! area.coords)
        {
            alert('no maps for coords for lot ' + lot_id);
            continue;
        }

        var coords = area.coords.split(",");
        var details = lots[lot_id];

        if(! details)
            continue;

        //makeMouseOver function takes 'n' arguments and returns a function which
        //will call showLot with those same 'n' arguments.

        //This is the same suggestion as pkaeding, only that I have exploited 'arguments'
        //property to make it simpler. 
        var makeMouseOver = function()
        {
            var creationArgs = arguments;
            return function() { showLot.apply(null, creationArgs); };
        }

        area.onmouseover = makeMouseOver(lot_id, area, coords, details, image, map, area, lots);

        // more code.
    }
}