I have a form marked up as
<form class="form1" method="post" action="form1.php" style="width:405px">
Ordinarily, I could access the action of the form in javascript by referring to the .action
of the form object, for example
document.forms[0].action
which would return the value
form1.php
However, if I have, as a ponent of the form, an item named "action", this "action" bees the content of the form's action. That is, if the form markup contains, for example,
<input name="action" type="hidden" value="check" />
Then
document.forms[0].action
returns the value
<input name="action" type="hidden" value="check" />
Now, I did work out how to get around this: by using
document.forms[0].getAttribute("action")
However, it's a nasty gotcha that confused me for too long. Is this a bug? A known gotcha of DOM management? Or should I just get into the habit of using .getAttribute()?
I have a form marked up as
<form class="form1" method="post" action="form1.php" style="width:405px">
Ordinarily, I could access the action of the form in javascript by referring to the .action
of the form object, for example
document.forms[0].action
which would return the value
form1.php
However, if I have, as a ponent of the form, an item named "action", this "action" bees the content of the form's action. That is, if the form markup contains, for example,
<input name="action" type="hidden" value="check" />
Then
document.forms[0].action
returns the value
<input name="action" type="hidden" value="check" />
Now, I did work out how to get around this: by using
document.forms[0].getAttribute("action")
However, it's a nasty gotcha that confused me for too long. Is this a bug? A known gotcha of DOM management? Or should I just get into the habit of using .getAttribute()?
Share edited Aug 7, 2013 at 15:50 Charles 51.4k13 gold badges106 silver badges144 bronze badges asked Aug 7, 2013 at 10:12 bugmagnetbugmagnet 7,7798 gold badges72 silver badges140 bronze badges 1-
1
Not a bug, the browser is emulating IE behaviour which started
form[elementname]
style - correct would beform.elements[elementname]
. However I had expected the attribute to shadow the input… – Bergi Commented Aug 7, 2013 at 10:24
3 Answers
Reset to default 7I would not call this a bug. This effect occurs, because attributes can be read by using element.attributename
and named inputs inside a form can be accessed in the same way, formelement.inputname
. If there is an attribute and an input with the same name, there is no guarantee which one will be used. It probably behaves differently in different browsers.
I personally use getAttribute
if I am reading a known attribute that was included in the markup or added using setAttribute
in JavaScript. For dynamic values like, for example, the checked
attribute of a checkbox, I don't use getAttribute
. But this is more a question of personal preference, I guess.
I am fighting this behaviour at the moment too (7 years later!)
It is very hacky, but you are able to access and call the action
getter directly by using the following.
Object.getOwnPropertyDescriptor(HTMLFormElement.prototype, 'action').get.call(document.forms[0]);
Having encountered this for a number of different form attrs, I finally worked up a general solution based on @threenplusone's answer.
The following code creates a safeForm()
function that wraps any Forms in a Proxy object which transparently "unmasks" any form properties that have been hidden ("id", "name", "action", etc). All other values are returned as-is.
// get property descriptor from wherever in chain
function getPropertyDescriptor (obj, name) {
let desc;
while(obj && !(desc = Object.getOwnPropertyDescriptor(obj, name))) {
obj = Object.getPrototypeOf(obj);
}
return desc;
}
// helpers for safeForm()
const isFormProxy = Symbol("isFormProxy"),
formHandler = {
get(form, prop) {
if (prop === isFormProxy) return true; // helper to detect proxy
let value = form[prop];
if (value instanceof HTMLElement && value.name === prop){
let desc = getPropertyDescriptor(HTMLFormElement.prototype, prop);
if(desc){
return desc.get.call(form);
}
// NOTE: ment out next line to include elements
// that aren't shadowing a form attribute.
return;
}
return value;
},
set(form, prop, value){
if(prop === isFormProxy) return;
form[prop] = value;
},
};
// frontend function
function safeForm (elem){
if(elem instanceof HTMLFormElement && !elem[isFormProxy]){
return new Proxy(elem, formHandler);
}
return elem;
}