gpt4 book ai didi

jquery - 为什么重用片段时事件监听器会消失?

转载 作者:行者123 更新时间:2023-12-01 08:34:30 25 4
gpt4 key购买 nike

我正在尝试重用这样的片段,但是事件监听器只工作一次,并且在片段被重用时不再存在:

https://codepen.io/dvtan/pen/dyyGVKm

let $home = $(`
<div>
<h1>Home page</h1>
<button class="btn btn-primary">Go to another page</button>
</div>`);

let $another = $(`
<div>
<h1>Another page</h1>
<button class="btn btn-primary">Go back</button>
</div>`);

$("body").html($home);

$home.find('.btn').on('click', () => {
$("body").html($another);
});

$another.find('.btn').on('click', () => {
$("body").html($home);
});

我认为直观地,事件监听器应该跟随片段,所以它们会像这样消失是没有意义的。为什么会发生这种情况,解决方法是什么?

最佳答案

这是您在 vanilla javascript 中尝试执行的操作:

var body = document.body;
var pages = {};
['home', 'other'].forEach(function(page){
var p = pages[page] = document.getElementById(page);
body.removeChild(p);
});

go = function(where){
body.innerHTML = '';
body.appendChild(pages[where]);
}

go('home');
<body>
<div id='home'>
<h1>Home Page</h1>
<button onclick="go('other')">Go to another Page</button>
</div>
<div id='other'>
<h1>Another Page</h1>
<button onclick="go('home')">Go Home</button>
</div>
</body>


我认为您期望调用 jQuery 方法 .html()go() 的效果相同上面的 Vanilla 代码中的方法正在做。扔掉旧的 HTML,并放入新的内容 - 实际上不是 HTML (=string),而是一个 dom 树,其中已经有处理程序。这种期望并非不合理——但 jQuery 并没有实现它,我将解释为什么会这样。

问题是 不是 新的东西有处理程序(jQuery 很好);问题在于 东西被扔掉。 .html() ,当扔东西时,不只是将它从 Dom 树中删除并忘记它;它假定(与 .detach() 不同,见下文)旧的东西不是您以后要重新插入的东西,而是垃圾,必须“焚烧”以免导致内存泄漏。

从文档:

When .html() is used to set an element's content, any content that was in that element is completely replaced by the new content. Additionally, jQuery removes other constructs such as data and event handlers from child elements before replacing those elements with the new content.



旧 dom 节点可能导致内存泄漏的方式是:通过 jQuery 方法与 dom 节点关联的数据 .data()不存储在节点本身中,而是存储在中央 jQuery 内部“缓存”中 - 节点只是对缓存有一个引用,因此节点可以找到它的数据。如果您以普通方式删除节点( parent.removeChild() ),旧垃圾节点的数据将保留在 jQuery 数据缓存中并在那里浪费空间。我不确定事件处理程序如何具有与上述类似的问题,但我确信它们的删除有类似的原因。进一步讨论内存泄漏问题 here在对 this question 的点赞评论中.

现在,在您的代码中,事件处理程序未设置为属性( onclick ),而是设置为 .on , 翻译成原版 addEventListener .而那些(参见 jQuery 文档的引用)在 .html() 时被焚毁。删除旧内容。

确实在某些情况下编码会更容易一些,如果 jQuery 在上面的焚化过程中省略,但是,由此产生的内存泄漏问题会更糟(因为与这里的小问题相比,它们更难找到),所以如果 jQuery 不辜负您的期望,那么坏的将超过好的。 (旁注:如果我的内存没有让我失望,那么旧的 jQuery 版本的行为就是这样,但不要引用我的话。)

解决方法是多方面的。最接近原始代码的版本(以及高于 Vanilla 版本,但使用 jQuery)是通过使用 .detach() 来规避焚烧。这是 jQuery 从 dom 树中删除某些东西而不焚烧它的方法。然后,确实,“事件处理程序跟随片段”。

像这样的东西:

var pages = {}, showing = null;
['home', 'other'].forEach(function(page){
pages[page] = $('#' + page).detach();
});
go = function(where){
if (showing){ pages[showing].detach(); }
$('body').append(pages[where]);
showing = where;
}

go('home')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<div id='home'>
<h1>Home Page</h1>
<button onclick="go('other')">Go to another Page</button>
</div>
<div id='other'>
<h1>Another Page</h1>
<button onclick="go('home')">Go Home</button>
</div>
</body>


我不确定我是否同意,但大多数人不使用 .detach() ,而是让焚化发生,然后重新创建处理程序,就像这样(并且,在这里,事件处理程序不会“跟随”):

let homeHTML = `
<div>
<h1>Home page</h1>
<button class="btn btn-primary">Go to another page</button>
</div>`;

let anotherHTML = `
<div>
<h1>Another page</h1>
<button class="btn btn-primary">Go back</button>
</div>`;

function setHome(){
$("body").html(homeHTML);
$("body").find('.btn').on('click', setAnother);
}
function setAnother(){
$("body").html(anotherHTML);
$("body").find('.btn').on('click', setHome);
}
setHome();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


jQuery 团队通过上述焚烧做出了正确的决定。但它确实有一个缺点,你的问题很重要。

上述两种解决方法都有一些不令人满意的地方(或者“丑陋”,如果你和我一样极端完美主义者的话)。 detach - append是两个步骤, .html - .on也是两步,但整个事情应该是一步。单页应用程序中的内容更改应该是类似于变量分配的单行代码。如(伪代码) library.set(body, 'otherPage')body <- otherPage或者。 jQuery .html()命令不是单行的。它不是,不是错误,但也不是选美比赛的获奖成就。

关于jquery - 为什么重用片段时事件监听器会消失?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58358216/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com