Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jquery.html() strange behavior with form

I have following html:

<div class="copy_me_text">
    <div>
        <input type="text" name="name" />
        <input type="hidden" name="id" />
    </div>
</div>

<div class="copy_me_hidden">
    <div>
        <input type="hidden" name="name" />
        <input type="hidden" name="id" />
    </div>
</div>

And following js code:

var $cloned_text = $('.copy_me_text').clone();
$cloned_text.find('input[name="name"]').val("SOMETHING");
$cloned_text.find('input[name="id"]').val("SOMETHING");
console.log($cloned_text.html());

var $cloned_hidden = $('.copy_me_hidden').clone();
$cloned_hidden.find('input[name="name"]').val("SOMETHING");
$cloned_hidden.find('input[name="id"]').val("SOMETHING");
console.log($cloned_hidden.html());

And output is strange for me:

<div>
    <input name="name" type="text">
    <input value="SOMETHING" name="id" type="hidden">
</div>
<div>
    <input value="SOMETHING" name="name" type="hidden">
    <input value="SOMETHING" name="id" type="hidden">
</div>

I create also jsFiddle example. Is it correct behavior? I don't understand, why in .html() function, value of input type="text" is not returned.

like image 912
Kasyx Avatar asked Jan 21 '13 13:01

Kasyx


4 Answers

This is not a strange jQuery behavior, its a strange DOM effect. jQuery.val() does nothing else than setting the value property of <input> element. By "property", I mean the DOM property and not the node attribute - see .prop() vs .attr() for the difference.

The .html() method, which returns the innerHTML serialisation of the DOM, is expected to show only attributes of the elements - their properties are irrelevant. This is the default behaviour, and when you want input values serialized you need to explicitly set them as attributes - $input.attr("value", $input.prop("value")).

So why did simple val() work on the hidden input elements? The reason is the HTML specification. There are reflecting IDL attributes, where the DOM property is coupled with the node attribute, but the value attribute is none of those. Yet, the value IDL attribute has special modes, in which it reacts differently. To cite the spec:

The attribute is in one of the following modes, which define its behavior:

value

On getting, it must return the current value of the element. On setting, it must set the element's value to the new value, set the element's dirty value [… and do a lot of other stuff].

default

On getting, if the element has a value attribute, it must return that attribute's value; otherwise, it must return the empty string. On setting, it must set the element's value attribute to the new value.

["default/on" and "filename" modes]

Spot the difference? And now, let's have a look at the states of the type attribute. And really, if we check the Bookkeeping details sections, we can notice that in the hidden state,

The value IDL attribute applies to this element and is in mode default

&dash; while in all other (textual) states the mode is "value".


TL;DR:

(Only) On <input type="hidden"> elements, setting the value DOM property (input.value = …, $input.val(…), $input.prop("value", …)) also sets the value attribute and makes it serializable for innerHTML/.html().

like image 196
Bergi Avatar answered Oct 25 '22 17:10

Bergi


That certainly looks like a bug in jQuery. You can force it to return by using the attr() function:

$cloned_text.find('input[name="name"]').attr('value', "SOMETHING");

See the updated Fiddle

like image 39
BenM Avatar answered Oct 25 '22 17:10

BenM


The html() function will return the html assign to the element. In your case you are assigning value to input text field.

Try this

$cloned_text.find('input[name="name"]').attr('value', "SOMETHING");
like image 24
nrsharma Avatar answered Oct 25 '22 18:10

nrsharma


It's not a jQuery bug.

As you can see in jQuery source code for the val method, they're just setting the value of the input element, which is a different thing from adding a new attribute to the element (which at the end, results in the value property change).

jQuery source (hooks is null in this context):

if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
    this.value = val;
}

So, to get what you what, you have to add a new val attribute to the element, using

.attr('value', "SOMETHING");

as others mentioned.

By using .attr the new attribute will be added to the element, and .html will return that as string.

like image 31
Alessandro Vendruscolo Avatar answered Oct 25 '22 16:10

Alessandro Vendruscolo