我正在空闲时间写一个简单的基于文本的游戏。它被称为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)
种函数。
答案 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元素,这就是它无效的原因。此外,setTimeout
在innerHTML
没有及时执行的情况下有助于提高可靠性。