views:

85

answers:

3

I am currently battling an IE JavaScript/DOM bug (what fun) and it has truly stumped me. The code in question copies some checkboxes into a form and needs to maintain their checked state. Problem is, IE (specifically IE8, though I'm guessing others as well) doesn't want to do that.

I've narrowed down the bug itself to a very small test case. Basically, things work correctly without a DOCTYPE on the page but they are broken when a DOCTYPE is present. I would have expected the opposite, but who knows with IE.


Following are the simplest possible test cases. For each of them: open the page in IE, toggle the checkbox, then click "TEST".

Does not produce the bug:

<input type="checkbox" id="broken">
<button id="break">TEST</button>
<script>
 document.getElementById('break').onclick = function() {
  alert(document.getElementById('broken').outerHTML);
 };
</script>

Link

Produces the bug:

<!DOCTYPE html>
<input type="checkbox" id="broken">
<button id="break">TEST</button>
<script>
 document.getElementById('break').onclick = function() {
  alert(document.getElementById('broken').outerHTML);
 };
</script>

Link

The bug occurs on valid pages (with <html>, <head>, <body>, etc) and whether or not the input is inside a form. In the "broken" case, outerHTML always reflects what was present on page load (if I have the input checked by default then it always alerts code with CHECKED, even if I uncheck it first). Things happen the same way if I wrap the input and use innerHTML. On the actual site I am using jQuery's .clone() method to do the copying; .clone() uses .outerHTML internally and that's how I narrowed this down.


My question is this: is there a way around this short of manually building new HTML myself? And does anyone have any idea WHY this happens in the first place (other than "IE SUX LOLZ")?

A: 

Use the DOM equivalent object of a checkbox http://www.javascriptkit.com/jsref/checkbox.shtml

Add it to your DOM form, dont use HTML and this dirty way.

For clarity:

var domCheckBox=document.getElementById('mycheckboxID');
var domForm=document.getElementById('myformID');
domForm.appendChild(domCheckBox);

This add your checkbox to your form and preserve the checked state (and other).

blow
This will move the original checkbox. I need to copy/clone it.
Matt Kantor
What about use domCheckBox.cloneNode() ?
blow
Matt Kantor
detachEvent -> cloneNode -> attachEvent
blow
If that method works and has none of the drawbacks mentioned in the jQuery's comments then .clone() should probably use it. I don't have time to come up with a full set of tests right now but if anyone else does it should be [submitted in a patch or ticket to jQuery](http://dev.jquery.com/).
Matt Kantor
(I assume you're talking about calling originalNode.attachEvent for all previously-attached handlers after cloning. I'm just not sure if this will always result in a final state identical to what you started with, or if there are issues with performance, events fired during cloning not working, etc.)
Matt Kantor
I say to detachEvent on originalNode before clone it.And after you can re-attach events to originalNode and cloned node too.I think this should works and resolve this trouble "Calling detachEvent on the clone will also remove the events from the orignal."Or use a framework with better events handling :)
blow
+2  A: 

There are problems in general with innerHTML and form properties. Not just in IE ((its worse in FF, it will NEVER update the checked property in innerHTML))...

You can try this to work around the issue in the linked question:

document.getElementById('break').onclick = function() {
  var broken = document.getElementById('broken');
  if (broken.checked) broken.setAttribute('checked','checked');
  else broken.removeAttribute('checked');
  alert(broken.outerHTML);
};
gnarf
The only reason my tests use outerHTML is because jQuery uses it for IE in [.clone()](http://james.padolsey.com/jquery/#v=1.4 I'll accept it unless someone posts something simpler soon. Thanks!
Matt Kantor
@Matt – Then that's a bug in clone() (nice source viewer, BTW). Just another question: why do you need that function? What are you trying to achieve with it?
Marcel Korpel
@Marcel: I've got a form that has a "subform" that appears in an overlay. The user is creating an event; the base form sets event attributes and the overlay allows inviting users to the event. After the user is done with the subform, its checkboxes are copied back to the base form (and hidden) so that the server knows who to invite after submit. I don't necessarily *need* to use .clone(), but it works everywhere else and is hella simple, so it'd be nice if it also worked in IE.
Matt Kantor
A: 

I ended up writing a wrapper for .clone() based off gnarf's answer:

$.fn.inputClone = function(events) {
    this.each(function() {
        this.attr('value', this.value);

        if(this.checked) this.setAttribute('checked', 'checked');
        else this.removeAttribute('checked');

        if(this.selected) this.setAttribute('selected', 'selected');
        else this.removeAttribute('selected');
    });
    return this.clone(events);
};

It's not very general-purpose (e.g. it won't work if you try to clone elements containing inputs), but for this case it seems to do the trick.

Matt Kantor