I have an issue with the way replace_html works in an RJS template.
This is a copy of a post on my blog (http://blog.craz8.com
http://blog.craz8.com/ ) that describes the problem and my working
solution to the problem.
If I have a collection of things that are output like this:
Or
I can use AJAX to insert a new ‘thing’ by implementing an RJS template
that does this, reusing the same partial layout:
page.insert_html :bottom, :partial => ‘thing’
but I can’t then replace the inserted thing by doing this, as this is
implemented as element.innerHTML:
page.replace_html “thing-id”, :partial => ‘thing’
There are ways around this, but they involve moving the outer element of
the thing out of the partial code, splitting the HTML across two files,
or removing and re-adding the element:
page.remove “thing-id”
page.insert_html :bottom, :partial => ‘thing’
I don’t like either of these ideas, so I came up with an improvement
that replaces the entire element in the DOM, similar to IE’s
element.outerHTML:
page.replace_html_element “thing-id”, :partial => ‘thing’
So, I’ve written an implementation of replace_html_element that comes in
two parts:
- The addition to the JavaScriptGenerator class to add a
replace_html_element method - An update to the Prototype Element implementation to perform the
client side update.
Add this code to the Application.rb (or in a separate file required by
Application.rb):
Update the JavaScriptGenerator to add our own functionality
module ActionView
module Helpers
module PrototypeHelper
class JavaScriptGenerator
def replace_html_element(id, *options_for_render)
html = render(*options_for_render)
record “Element.replace(#{id.inspect}, #{html.inspect})”
end
end
end
end
end
Add this code to your application javascript file:
// Extend the object for our RJS extension to work
Object.extend(Element, {
replace: function(element, html) {
var el = $(element);
if (el.outerHTML) { // IE
el.outerHTML = html.stripScripts();
} else { // Mozilla
var range = el.ownerDocument.createRange();
range.selectNodeContents(el);
el.parentNode.replaceChild(range.createContextualFragment(html.stripScri
pts()), el);
}
setTimeout(function() {html.evalScripts()}, 10);
}
}
);