AJAX+jQuery单页应用动态DOM加载后append操作与事件问题
嘿,我完全懂你碰到的这个麻烦——你的forEach循环跑得太早了,这时候hello.html里的下拉框还没被注入到index.html的#content里呢,自然找不到元素,填充不了选项对吧?而且我还发现你代码里有个小拼写错误,得先提一下:hello.html里的select是id="typeUser",但hello.js里写的是$('select#userType'),ID差了个字母,这也会导致找不到元素哦!
问题根源
你把hello.js直接放在index.html里提前加载了,当页面初始化执行这段JS时,#content还是空的,select#typeUser根本不存在,所以append操作等于对着空气干活,完全没效果。而你的点击事件能用,是因为用了事件委托(绑定在document上),后来动态添加的按钮触发点击时,事件会冒泡到document被捕获到。
解决方案:等DOM元素加载完成后再执行填充逻辑
我们可以把填充下拉框的代码封装成函数,然后在AJAX成功加载hello.html、把内容注入到#content之后,再调用这个函数。这样就能保证元素已经存在了。
步骤1:修正拼写错误并封装填充函数
修改hello.js的内容:
var typeUser = ["user" , "administrator"]; // 封装填充下拉框的函数 function populateUserTypeSelect() { // 先清空原有选项(避免重复加载时重复添加) $('#typeUser').empty().append('<option value="empty">choose type of user</option>'); typeUser.forEach(e => { $('#typeUser').append('<option>' + e + '</option>'); }); } // 事件委托保持不变 $(document).on('click','#btn',function(e) { alert("hello world"); });
步骤2:在AJAX加载回调中调用函数
假设你用来加载页面的AJAX代码是类似这样的,修改它,在加载完成后判断如果是hello.html就调用填充函数:
$('ul li a').on('click', function(e) { e.preventDefault(); // 阻止默认跳转 var targetUrl = $(this).attr('href'); $('#content').load(targetUrl, function(response, status, xhr) { if (status === "success" && targetUrl === './hello.html') { // 页面加载完成,现在可以填充下拉框了 populateUserTypeSelect(); } }); });
额外思路:通用化处理
如果你的SPA有很多页面都需要类似的初始化逻辑,可以给每个动态页面的内容加一个自定义属性,比如在hello.html的最外层加个data-page="hello",然后在AJAX回调里根据这个属性判断要执行哪个初始化函数,这样扩展性更好:
<!-- hello.html 修改后 --> <div data-page="hello"> <select id="typeUser"> <option value="empty">choose type of user</option> </select> <button id="btn">say hello world</button> </div>
然后AJAX回调里:
$('#content').load(targetUrl, function(response, status, xhr) { if (status === "success") { var pageName = $('#content').find('[data-page]').attr('data-page'); if (pageName === 'hello') { populateUserTypeSelect(); } // 其他页面的初始化函数也可以在这里添加判断 } });
这样调整之后,当你点击hello链接,页面内容加载完成后,下拉框就会被正确填充啦!
内容的提问来源于stack exchange,提问作者ekka

