Rails rails 异步响应 渲染 js.erb 的问题

tsinghan · 2014年07月19日 · 最后由 flowerwrong 回复于 2014年07月27日 · 4411 次阅读

情景如下 服务端响应后渲染一个 js.erb 文件 内容如下:

$('#msg').append("<%= escape_javascript(render partial: 'share/messages', locals: {message: "ok" } )%>")

share/messages 的内容如下:

<div class="notice" id="failed">
  <%= message %>
  <div></div>
</div>

页面上有段 js 是控制 share/messages 内容的

//点击差号关闭notice栏
$(".notice>div").click(function(){

$(".notice").fadeOut("fast");   

});

不响应异步请求前 js 是好用的,但是响应并渲染 js.erb 之后,这段 js 失效了,我在浏览器的 console 里面手动执行这段 js 后,才有效,是我遗漏了 什么东西?

顺序错了,你做 $(".notice>div")绑定的时候,这个元素还没有出现在页面呢。 你要把绑定操作移到你异步加载成功的 callback 里面

#1 楼 @guyanbiao 奥奥 我也考虑过是顺序问题,如果放到 callback 里面 用 js.erb 这种怎么写?还是必须用服务端返回 json,在客户端的 callback 里面实现?

如一楼所说,适应为你绑定事件对时候,“.notice>div”这个 dom 还没有出现在页面,所以 $(".notice>div") 返回为 [],事件就没有绑定。

解决方法两种:

  1. 如一楼,把时间绑定写到 js.erb 中,的 append 操作时候

    $('#msg').append("<%= escape_javascript(render partial: 'share/messages', locals: {message: "ok" } )%>")
    $(".notice>div").click(function(){
    $(".notice").fadeOut("fast");   
    });
    
  2. 如下,通过 "#msg" 这个父 DOM 来进行事件绑定。原理参照 http://api.jquery.com/on/

    //点击差号关闭notice栏
    $("#msg").on("click", ".notice>div", function(){
    $(this).closest(".notice").fadeOut("fast");
    });
    

个人倾向于第二种,click 事件只需绑定一次。而第一种则需要每 append 一次消息,都要绑定一次。

#3 楼 @reyesyang 直接委托到 document 最好,用上 turbolinks 也不会失效

delegate 到父级元素上

#4 楼 @saiga 绑定到 document? 怎么写? #5 楼 @tumayun 你的意思是

$(".notice>div").click(function(){
    $(".notice").fadeOut("fast");   
    });

绑定到 .notice 的父元素上?

#3 楼 @reyesyang 意思就是在 js.erb 里面重写 js?

#6 楼 @TsingHan

  1. 建议给 .notice>div 这个 dom 加一个 class,比如 .notice-close
  2. 用 on 函数将事件委托到 document 对象,浏览器会通过事件冒泡提升事件直到 document。因为其他事件绑定函数都是基于 on,所以这种方式是 jQ 首推。(在整个生命周期只要执行一次,多次会造成事件重复和内存泄露)
$(document).on('click', '.notice-close', function(e) {

});

#8 楼 @saiga thks, 采用了这种办法果然可以了,我之前的做法是 js html 加载执行顺序的原因吗?没想通渲染一个 js.erb 后,js 就找不到 对应 div 元素了 请指教。再者,绑定 document 这种方式 为什么可以找到 div 元素?

#9 楼 @TsingHan 如果 div 已经插入 dom tree,那 js 是肯定能找到的。你这里没有触发事件是因为新进来的 dom 没有绑定回调函数。

WHY?

$('.notice>div') 这段代码是找出所有 .notice 下第一层 div 的元素

然后,用 click 函数绑定事件回调。这个动作会将事件附加到 .notice>div 这个对象上,也就是说,$(',notice>div').click 只会对执行代码时页面存在的元素生效。

当你动态生成新对象时,代码已经执行了,所以这个新对象没有绑定回调,也就无法运行了。

=================

而用 on 函数委托事件是利用浏览器的冒泡机制

在页面里,用户每点击一次,浏览器就会提升事件到 document 对象。换句话说,on 函数并不是找到 div,而是 div 元素在对祖先节点逐个呼喊:“老子被人点啦!”,直到有人对他作出回应,也就是用 on 函数捕获了事件。

思绪有点混乱,或许有点说不清楚。如果哪里看不明白指出来我尽量再改改

#11 楼 @saiga 奥 我明白我之前错的原因了,因为当执行 js.erb 时 用 $('#msg').append 进去的元素,和之前页面上的但是不是同一个元素,也就是这块插入的元素,并没有绑定 js 事件,所以无响应。 而 on 这种冒泡机制,我也可以像 #3 楼 @reyesyang#6 楼 @TsingHan 说的那样。而不必非要绑定到 document 上吧

#12 楼 @TsingHan 的确不需要非得绑定到 document 上,关键在于用不用 turbolink,或者说委托的父元素会不会被清理。不过即使委托到 #msg 元素上,不手动停止事件还是会继续向上冒泡直到 document

#13 楼 @saiga 奥 明白了 我去搜索 这个冒泡机制的原理,多谢!

用 off 解除事件绑定也是个好习惯,避免造成重复或者内存泄露 😄

live 绑定也可以。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号