新手问题 为何使用了 Turbolinks 以后,jquery_ujs 里的 Event 被重复 bind 了多次?

adamshen · 发布于 2016年1月10日 · 最后由 qinfanpeng 回复于 2016年1月11日 · 1397 次阅读
20859

是这样,我写了一个简单的form,使用remote: true改成了用ajax进行提交。提交以后查看日志,发现Rails收到了数次重复的ajax提交。

接着我打开Firebug查看提交页面,发现与提交有关的click事件被重复绑定了四次。虽然Turbolinks不会重新引入jquery_ujs,但是每次切换页面,似乎jquery_ujs都会重新被执行一遍。

要如何才能避免这种现象?

这是含有form的new.html.erb的代码

<div class="container">
  <h2 class="col-md-offset-5">新增电脑</h2>

  <div class="col-md-offset-3">
    <%= form_for @computer, url: computers_path, remote: true,
                 html: {class: "form-horizontal"} do |f| %>
        <%= render partial: "form", locals: {f: f} %>
        <div class="form-group">
          <div class="col-md-offset-3">
            <%= f.submit("创建", class: "btn btn-default") %>
          </div>
        </div>
    <% end %>
  </div>
</div>

layout代码

<!DOCTYPE html>
<html>
<head>
  <title>Manage</title>
  <nav class="navbar navbar-default navbar-fixed-top">
      <%= top_nav class: "nav navbar-nav" do |li|
        li << link_to("首页", root_path)
        li << link_to("通讯录", contacts_path)
        li << link_to("考勤记录", attends_path)
        li << link_to("固定资产", assets_path(default_company))
        li << link_to("个人借款", loans_path(default_company))
        li << link_to("人事系统", employees_path)
        li << link_to("电脑管理", computers_path)
        li << link_to("项目清单", projects_path)
      end %>

    <ul class="nav navbar-nav navbar-right">
      <% if user_signed_in? %>
          <li><%= link_to "退出登录", logout_path %></a></li>
      <% else %>
          <li><%= link_to "登录系统", new_user_session_path %></a></li>
      <% end %>
    </ul>
  </nav>
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

这是application.js里的

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
//= require bootstrap-sprockets

除了上面这些以外,没有写任何其他的前端代码。

共收到 12 条回复
3790

看看你的代码里是否这样的代码 $(document).on "page:load", ->,其中的page:load每次因 Turbolinks引起的切换页面都会执行里面的逻辑。我猜想你需要的是这样的代码 $(document).on "ready", ->,这个只在第一次加载后 dom ready 才执行,后面因 Turbolinks引起的切换页面都不会再执行了。

20859

#1楼 @qinfanpeng 恩,感谢解答。我再补充和追问一下,这里我并没有自己写任何js代码,只是利用remote: true来完成ajax提交。这是没有使用Turbolinks之前所有的click事件,都是jquery_ujs里绑定的,很正常。

下面是使用了Turbolinks之后的click事件,每个相同的事件都被重复绑定了三次,导致我在网页上点下提交,会触发三次ajax提交。

我不解的是为何相同的事件会重复绑定三次,在我未切换页面前,是正常的,但一切换页面,似乎click事件就会被多绑定一次。

1

贴代码。

20859

#3楼 @rei 好的,已更新。

1

app/asserts/javascripts 目录还有什么文件?

将 application.js 删到剩下:

//= require jquery
//= require jquery_ujs
//= require turbolinks

再试试。

20859

#5楼 @rei 试了下,还是不行。

app/asserts/javascripts目录下剩下的是每个controller对应的coffee脚本,都是空白的,没有在其中增加代码。

我怀疑是某个Gem和turbolinks冲突,于是new了一个新的rails demo,只加载rails基本的gem。然后在layout里只写一个导航栏,发现也依然有这个问题。

看了看Trubolinks的这段切页代码

changePage = (title, body, csrfToken, runScripts) ->
  triggerEvent EVENTS.BEFORE_UNLOAD
  document.title = title
  document.documentElement.replaceChild body, document.body
  CSRFToken.update csrfToken if csrfToken?
  setAutofocusElement()
  executeScriptTags() if runScripts
  currentState = window.history.state
  progressBar?.done()
  triggerEvent EVENTS.CHANGE
  triggerEvent EVENTS.UPDATE

executeScriptTags = ->
  scripts = Array::slice.call document.body.querySelectorAll 'script:not([data-turbolinks-eval="false"])'
  for script in scripts when script.type in ['', 'text/javascript']
    copy = document.createElement 'script'
    copy.setAttribute attr.name, attr.value for attr in script.attributes
    copy.async = false unless script.hasAttribute 'async'
    copy.appendChild document.createTextNode script.innerHTML
    { parentNode, nextSibling } = script
    parentNode.removeChild script
    parentNode.insertBefore copy, nextSibling
  return

根据我的理解, Turbolinks不会重新加载head里已载入过的js脚本,只会再运行一遍body里js语句。但是我打开firebug发现,每次我切换页面,浏览器就重新从本地的bfcache里get了一次head里的js脚本。另外我又看了jquery_ujs的代码

if ( $.rails !== undefined ) {
  $.error('jquery-ujs has already been loaded!');
}

jquery_ujs里的既然有防止重复载入的语句,那么为何里面的事件还是会被重复bind?是否和浏览器的cache机制有关?

1

把 head 里面那段 nav 删掉看看,我怀疑是不合语法浏览器自动修正把 script tag 归入 body 了。

1

果然

<html>
  <head>
    <nav>nav</nav>
    <script></script>
  </head>
  <body>
  </body>
</html>

浏览器修正

可显示元素要放 body 里。

3790

#8楼 @rei 厉害,他这个问题我看了半天没眉目,只是感觉把 nav 标签放到 head 里很奇怪,没想到会是这样。还是你们这些前辈有经验。:plus1:

20859

#8楼 @rei 哇。哈哈哈哈。移到body里,真的正常了。你太帅了!

感谢大神相助,前端好多我的知识盲点,看来要恶补才行。

20859

#9楼 @qinfanpeng 你也很有经验哒,只是我这样的乌龙太少人会犯了。 😪

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