13

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.

Kasyx
  • 3,170
  • 21
  • 32
  • 1
    isolated behavior: http://jsfiddle.net/xRjPm/4/ – jbabey Jan 21 '13 at 14:09
  • 2
    @jbabey: it is different since you're cloning an element with id, duplicate ids can produce unexpected behavior as expected. – Salman A Jan 21 '13 at 14:11
  • 1
    @SalmanA the clone is never added to the DOM, and the duplicate IDs do not exist in the same container we're using as a context, so it doesn't really matter. – jbabey Jan 21 '13 at 14:16

5 Answers5

8

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().

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
6

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

Anujith
  • 9,370
  • 6
  • 33
  • 48
BenM
  • 52,573
  • 26
  • 113
  • 168
3

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");
BenM
  • 52,573
  • 26
  • 113
  • 168
nrsharma
  • 2,532
  • 3
  • 20
  • 36
2

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.

Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
1

A good workaround for this problem would be to use the attr() function instead of the val().

So, instead of

$cloned_text.find('input[name="name"]').val("SOMETHING");

Write

$cloned_text.find('input[name="name"]').attr("value", "SOMETHING");
Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
Omer M.
  • 630
  • 7
  • 17