在innerHTML之后,addEventListener无法正常工作

时间:2013-12-11 03:07:04

标签: javascript html

我正在空闲时间写一个简单的基于文本的游戏。它被称为TARDIS飞行(神秘博士!),我正在主菜单上工作。

因此,在设置主菜单的addMainMenuListeners之后,我正在使用函数addEventListener来添加innerHTML的所有事件监听器。
一切正常,直到我从其中一个子菜单返回主菜单。然后,我发现这些按钮不再起作用。
在设置addMainMenuListeners之后我正在调用innerHTML,但即使我这样做,我也是在控制台中执行,我检查,没有事件。

码:
在我的主要javascript文件中:

function addMainMenuListeners()
{
    if($("start")) $("start").addEventListener("click", startGame);
    if($("instructions")) $("instructions").addEventListener("click", instructions);
    if($("settings")) $("settings").addEventListener("click", settings);
    if($("back")) $("back").addEventListener("click", resetMainMenu);
    if($("volume")) $("volume").addEventListener("change", function(){saveAndChangeVolume($("volume").value);});
}

function instructions()
{
    $("mainmenu").innerHTML = "<h1>Instructions</h1><p>Fly your TARDIS through the time vortex using the console.  Make sure that you use the correct materialization codes.  Try to keep the time-space coordinates close to the values of the ones given.  AND DON'T CREATE PARADOXES, WHATEVER YOU DO!</p><button id='back'>Back</button>";
    addMainMenuListeners();
    return true;
}

function settings()
{
    $("mainmenu").innerHTML = "<h1>Volume</h1><span>1</span><input type='range' id='volume' min='1' max='100' /><span>100</span><br><button id='back'>Back</button>";
    addMainMenuListeners();
    loadVolume();
    return true;
}

function resetMainMenu()
{
    $("mainmenu").innerHTML = "<h1>TARDIS Flight</h1><button id='start'>Start!</button><button id='instructions'>Instructions</button><button id='settings'>Settings</button>";
    addMainMenuListeners();
    return true;
}



在我的HTML文件中:

<div id="mainmenu">
<h1>TARDIS Flight</h1>
<button id="start">Start!</button>
<button id="instructions">Instructions</button>
<button id="settings">Settings</button>
</div>

如果您需要任何澄清,请询问。

编辑:很明显,没有人得到我的意思。正如您在代码中看到的那样,我在执行innerHTML之后正在读取事件监听器。我根本看不到添加的事件,该功能正在触发但不添加事件。
另外,我使用的是自定义$函数,只是return document.getElementById(id)种函数。

3 个答案:

答案 0 :(得分:5)

检查$()是否使用了任何缓存。如果它将旧引用缓存到元素,则在设置innerHTML时,$("id")将返回对无效引用的引用。

[编辑]即使在HTML DOM中不再可见,引用也更有可能有效。因此,修改分离的元素可以正常工作,但由于它们与DOM分离,因此无效。

答案 1 :(得分:1)

Sukima psychic ability's抓住了主要问题:你的自定义$函数(替换document.getElementById)使用了缓存机制。

经过一些测试(出于个人的好奇心),只要你有一些元素的引用,就可以访问,即使在elm.parentNode.removeChild之后也是如此elm.parentNode.innerHTML重写(至少在FF中)。

换句话说:每次添加的事件只是错误的/旧的/前一个元素而不是新的元素。否则,也会出现错误,这些元素不存在,因此没有addEventListener方法。

请参阅这个粗略的测试小提琴以获得证明:http://jsfiddle.net/G28Lu/


我玩了一个$函数(因为你还没有发布你的函数)并给它一个'refresh'标志来配合可选的缓存机制:

var $=function(/*cache_enabled*/c){  // getElementById with optional caching & refresh
   return c ?( c={}, function(s,r){return (!r && c[s]) || (c[s]=document.getElementById(s));} 
           ):( function(s){return document.getElementById(s);} );
}(true); // or nothing or 0 etc to disable the cache

注意:动态创建的函数很慢(出于某种原因)因此它有2个函数,因此浏览器可以优化运行它们(另一个应该由垃圾收集器清除)。

要请求新的,只需添加一个评估为true的标志作为第二个参数,例如:$('elm_id', 1)

然后我稍微修改了你的addMainMenuListeners函数,首先测试是否存在新的getElementById,然后通过新更新的缓存引用添加eventListener(所以,基本上我在你的例程流程中没有改变)

格式化以强调改变的内容及其工作原理

function addMainMenuListeners(){
   $(   'start'    , 1) && $(   'start'    ).addEventListener('click', startGame);
   $('instructions', 1) && $('instructions').addEventListener('click', instructions);
   $(  'settings'  , 1) && $(  'settings'  ).addEventListener('click', settings);
   $(   'back'     , 1) && $(    'back'    ).addEventListener('click', resetMainMenu);
   $(  'volume'    , 1) && $(   'volume'   ).addEventListener('change', function(){ 
                                               saveAndChangeVolume($('volume').value); 
                                             });
}

最后,将2个以上的函数和其余的函数/ html放到这个小提琴中会产生一个有效的结果:http://jsfiddle.net/xHUGu/
注意:我必须替换虚函数startGame,否则会出现致命错误。丢失的音量函数并不重要。

我想指出,这不是你的界面的方式,你可以节省很多工作来保存自己和浏览器。您可能希望查看div(包含html的子部分)并切换它们以便只有一个可见。喜欢标签。 (提示谷歌查询)。

信用仍然归于Sukima(“这一方面的力量很强”),但我觉得分享对你的问题的正确解释(有证据)是一个好主意,而不是浪费所做的工作(出于好奇)。

希望这有帮助!

答案 2 :(得分:0)

禁用$功能上的缓存功能。它引用了一个被破坏的HTML元素,这就是它无效的原因。此外,setTimeoutinnerHTML没有及时执行的情况下有助于提高可靠性。