简体   繁体   中英

Trouble on binding links with HTML5 'data-*' attributes

I am using Ruby on Rails 4 and I have the following code:

# view.html.erb

link_to("Title 1", url1, :remote => true, :method => :patch, :data => {:type => :html, :custom => true}
# <a rel="nofollow" href="/url1" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 1</a>

link_to("Title 2", url2, :remote => true, :method => :patch, :data => {:type => :html, :custom => true}
# <a rel="nofollow" href="/url2" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 2</a>

link_to("Title 3", url3, :remote => true, :method => :patch, :data => {:type => :html, :custom => true}
# <a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 3</a>


# application.js

$(document).ready(function() {
  $('a[data-remote][data-custom]').bind({
    click: function(event) {
      $(document).on('ajax:success', function(xhr, data, status) {
        $(this).replaceWith(data);
      });
    }
  });
});

For instance, when the link Title 1 is clicked then, on success, the response/returned data is the following link (the same behavior applies when click links Title 2 and Title 3 ):

link_to("Replaced 1", url1, :remote => true, :method => :patch, :data => {:type => :html, :custom => true}
# <a rel="nofollow" href="/url1" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 1</a>

As you can see the response/returned link is almost the same as the original link (that is, it has data-* attributes as-like the original clicked link) and so the JavaScript binding should happen even for that link after it replaces the DOM.

However, if I continue to click other links as-like the Title 2 then both the Title 1 and the Title 2 links are replaced on the front-end content resulting in the following whole HTML code:

<a rel="nofollow" href="/url2" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 2</a>
<a rel="nofollow" href="/url2" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 2</a>
<a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 3</a>

For completeness, when I click Title 3 then the resulting in the following whole HTML code:

<a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 3</a>
<a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 3</a>
<a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Replaced 3</a>

It seems that the JavaScript binding isn't working as expected (note repeated Replaced <N> links). I do not understand why it could happen but the expected behavior is to just replace one link each time that link is clicked.

What is the problem? How can I solve it?

The bind function attaches the event handler to the actual element at the time of calling. So when you recreate the link, it doesn't have an event handler attached for click. Instead, you should use either .on(..) or .delegate(..), and attach the event handler itself to a wrapper element above the links.

Conversely, you are creating a new ajax success handler every time a link is clicked, so they build up. I'm not sure how your AJAX functionality is implemented, but you should either have the ajax:success event attached only to the function/object consuming it, or have it attached to the document only once, where you would need to pass in the element that was clicked to start the request.

Untested example code:

<script type="text/javascript">
    $(document).ready(function () {
        $("#link-list").on('ajax:success', "a[data-remote][data-custom]", function(xhr, data, status) {
            $(this).replaceWith(data);
        });
    });
</script>
 <div id="link-list"> <!-- // wrapping element to attach event handlers to -->
    <a rel="nofollow" href="/url1" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 1</a>
    <a rel="nofollow" href="/url2" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 2</a>
    <a rel="nofollow" href="/url3" data-type="html" data-custom="true" data-remote="true" data-method="patch">Title 3</a>
</div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM