IE is not submitting dynamically added form elements - javascript

I wrote some JavaScript to allow editing a list of items within an HTML form, including adding and removing items. Got it working in Firefox. When trying it in Internet Explorer, I found that any added items were not being submitted with the form.
Long story short... lots of simplification, debugging, figured out what line is triggering IE to ignore the new form input. So the behavior problem is solved.
But now I must ask: Why? Is this an IE bug?
Here is the simplified code:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
function add() {
div = document.getElementById("mylist");
// *** Adding text here works perfectly fine. ***
div.innerHTML += " ";
e = document.createElement("input");
e.setAttribute("type", "text");
e.setAttribute("name", "field3");
e.setAttribute("value", "--NEWVALUE--");
div.appendChild(e);
// *** Adding text here works perfectly fine in Firefox, but for
// Internet Explorer it causes field3 to not be submitted. ***
//div.innerHTML += " ";
}
</script>
</head>
<body>
<form action="" method="get">
<div id="mylist">
<input type="text" name="field1" value="value1" />
<input type="text" name="field2" value="value2" />
</div>
<a href="javascript:" onclick="add()" />Add</a>
<input type="submit" value="Submit" />
</form>
</body>
</html>
To try it out, do the obvious: load in IE, click Add, click Submit, look what's in the address bar. If you uncomment the last line in add(), IE will suddenly stop reporting field3. It works fine either way in Firefox.
Any ideas? A curious mind wants to know. (And how would I add text there if needed, in a portable fashion, so IE is happy?)

Is this an IE bug?
Seems so. When you create an <input> element through DOM methods, IE doesn't quite pick up the ‘name’ attribute. It's sort-of-there in that the element does submit, but if you try to get an ‘innerHTML’ representation of the element it mysteriously vanishes. This doesn't happen if you create the element by writing directly to innerHTML.
Also if you use DOM Level 0 form navigation methods, like ‘myform.elements.x.value’, access through the ‘elements’ array may not work (similarly the direct ‘myform.x’ access some people misguidedly use). In any case these days you might prefer getElementById().
So either use innerHTML or use DOM methods; best not to mix them when creating form fields.
This is documented (see ‘Remarks’) and finally fixed in IE8.
In any case, never do:
div.innerHTML+= '...';
This is only syntactical sugar for:
div.innerHTML= div.innerHTML+'...';
In other words it has to serialise the entire child HTML content of the element, then do the string concatenation, then re-parse the new string back into the element, throwing away all the original content. That means you lose anything that can't be serialised: as well as IE's bogus half-created ‘name’ attributes that also means any JavaScript event handlers, DOM Listeners or other custom properties you have attached to each child element. Also, the unnecessary serialise/parse cycle is slow.

IE is very picky about changing some built-in properties at runtime. For example, the name of an input element cannot be changed while set.
Two things I would try if I were you:
Instead of using setAttribute(), try setting the name, type and value properties explicitly:
e.name = "text";
If this doesn't work, you may have to include all these attributes into the document.createElement() call:
var e = document.createElement("<input type='text' name='field'>");
this may actually throw an exception in some browsers. So the best cross browser way to go would be:
.
var e;
try {
e = document.createElement("<input type='text' name='field'>");
} catch (ex) {
e = document.createElement("input");
e.type = 'text';
e.name = 'field';
}
e.value = 'value';

Thank you bobince and levik for your answers. Using those, and some more experimentation, here are my conclusions:
Yes it is an IE bug.
IE 8 fixes the bug according to Microsoft: "Internet Explorer 8 and later can set the NAME attribute at run time on elements dynamically created with the createElement method."
The bug is this: Calling e.setAttribute("name", "field3") only kind-of sets the name. It will work if nothing else happens to the element, but if requested to serialize, the name is not serialized. So when I said innerHTML += " " that forced a serialization, which lost the name, so it was not recovered upon deserialization. No name, no inclusion in form submission.
Workaround #1: e = document.createElement("<input name='field3' />") would work, even when faced with serialization.
Workaround #2: Rather than adding text using innerHTML +=, I can append a text element like this: div.appendChild(document.createTextNode(" "));. I had figured there must be a better way of adding text, and now I know it :-).
Cheers,--jsf

Related

How do I set specific key for keydown event listener? [duplicate]

I am trying to use an HTML button to call a JavaScript function.
Here's the code:
<input type="button" value="Capacity Chart" onclick="CapacityChart();">
It doesn't seem to work correctly though. Is there a better way to do this?
Here is the link:http://projectpath.ideapeoplesite.com/bendel/toolscalculators.html click on the capacity tab in the bottom left section. The button should generate an alert if the values are not changed and should produce a chart if you enter values.
There are a few ways to handle events with HTML/DOM. There's no real right or wrong way but different ways are useful in different situations.
1: There's defining it in the HTML:
<input id="clickMe" type="button" value="clickme" onclick="doFunction();" />
2: There's adding it to the DOM property for the event in Javascript:
//- Using a function pointer:
document.getElementById("clickMe").onclick = doFunction;
//- Using an anonymous function:
document.getElementById("clickMe").onclick = function () { alert('hello!'); };
3: And there's attaching a function to the event handler using Javascript:
var el = document.getElementById("clickMe");
if (el.addEventListener)
el.addEventListener("click", doFunction, false);
else if (el.attachEvent)
el.attachEvent('onclick', doFunction);
Both the second and third methods allow for inline/anonymous functions and both must be declared after the element has been parsed from the document. The first method isn't valid XHTML because the onclick attribute isn't in the XHTML specification.
The 1st and 2nd methods are mutually exclusive, meaning using one (the 2nd) will override the other (the 1st). The 3rd method will allow you to attach as many functions as you like to the same event handler, even if the 1st or 2nd method has been used too.
Most likely, the problem lies somewhere in your CapacityChart() function. After visiting your link and running your script, the CapacityChart() function runs and the two popups are opened (one is closed as per the script). Where you have the following line:
CapacityWindow.document.write(s);
Try the following instead:
CapacityWindow.document.open("text/html");
CapacityWindow.document.write(s);
CapacityWindow.document.close();
EDIT
When I saw your code I thought you were writing it specifically for IE. As others have mentioned you will need to replace references to document.all with document.getElementById. However, you will still have the task of fixing the script after this so I would recommend getting it working in at least IE first as any mistakes you make changing the code to work cross browser could cause even more confusion. Once it's working in IE it will be easier to tell if it's working in other browsers whilst you're updating the code.
I would say it would be better to add the javascript in an un-obtrusive manner...
if using jQuery you could do something like:
<script>
$(document).ready(function(){
$('#MyButton').click(function(){
CapacityChart();
});
});
</script>
<input type="button" value="Capacity Chart" id="MyButton" >
Your HTML and the way you call the function from the button look correct.
The problem appears to be in the CapacityCount function. I'm getting this error in my console on Firefox 3.5: "document.all is undefined" on line 759 of bendelcorp.js.
Edit:
Looks like document.all is an IE-only thing and is a nonstandard way of accessing the DOM. If you use document.getElementById(), it should probably work. Example: document.getElementById("RUnits").value instead of document.all.Capacity.RUnits.value
This looks correct. I guess you defined your function either with a different name or in a context which isn't visible to the button. Please add some code
Just so you know, the semicolon(;) is not supposed to be there in the button when you call the function.
So it should just look like this: onclick="CapacityChart()"
then it all should work :)
One major problem you have is that you're using browser sniffing for no good reason:
if(navigator.appName == 'Netscape')
{
vesdiameter = document.forms['Volume'].elements['VesDiameter'].value;
// more stuff snipped
}
else
{
vesdiameter = eval(document.all.Volume.VesDiameter.value);
// more stuff snipped
}
I'm on Chrome, so navigator.appName won't be Netscape. Does Chrome support document.all? Maybe, but then again maybe not. And what about other browsers?
The version of the code on the Netscape branch should work on any browser right the way back to Netscape Navigator 2 from 1996, so you should probably just stick with that... except that it won't work (or isn't guaranteed to work) because you haven't specified a name attribute on the input elements, so they won't be added to the form's elements array as named elements:
<input type="text" id="VesDiameter" value="0" size="10" onKeyUp="CalcVolume();">
Either give them a name and use the elements array, or (better) use
var vesdiameter = document.getElementById("VesDiameter").value;
which will work on all modern browsers - no branching necessary. Just to be on the safe side, replace that sniffing for a browser version greater than or equal to 4 with a check for getElementById support:
if (document.getElementById) { // NB: no brackets; we're testing for existence of the method, not executing it
// do stuff...
}
You probably want to validate your input as well; something like
var vesdiameter = parseFloat(document.getElementById("VesDiameter").value);
if (isNaN(vesdiameter)) {
alert("Diameter should be numeric");
return;
}
would help.
Your code is failing on this line:
var RUnits = Math.abs(document.all.Capacity.RUnits.value);
i tried stepping though it with firebug and it fails there. that should help you figure out the problem.
you have jquery referenced. you might as well use it in all these functions. it'll clean up your code significantly.
I have an intelligent function-call-backing button code:
<br>
<p id="demo"></p><h2>Intelligent Button:</h2><i>Note: Try pressing a key after clicking.</i><br>
<button id="button" shiftKey="getElementById('button').innerHTML=('You're pressing shift, aren't you?')" onscroll="getElementById('button').innerHTML=('Don't Leave me!')" onkeydown="getElementById('button').innerHTML=('Why are you pressing keys?')" onmouseout="getElementById('button').innerHTML=('Whatever, it is gone.. maybe')" onmouseover="getElementById('button').innerHTML=('Something Is Hovering Over Me.. again')" onclick="getElementById('button').innerHTML=('I was clicked, I think')">Ahhhh</button>

IE10 inserting blank text DOM entries

I have a piece of Javascript code that locates the single table in the DOM then tries to manipulate its first child, the thead (actually, it iterates though the children of that child, the tr entries but that's not important to the question). The code to do this is:
var tableNode = document.getElementById("table").firstChild;
This works fine in Firefox ESR (10/17/24) and IE9 but fails in IE10, and the reason appears to be because IE10 is inserting weird DOM entries and it's one of those I'm picking up with firstChild instead of the desired thead. I base this on the DOM dump below along with the fact that tableNode.data is set to the string type.
The DOM in IE10 compatibility mode (where it also works) looks like this:
and you can see that the DOM indeed looks sensible. However, examining the DOM in normal IE10 mode shows this:
For what it's worth, Chrome gives me:
and FF17esr gives me:
neither of which seem to have the empty text elements.
Now, I can run the site in compatibility mode but that's an annoying thing to have to tell all our customers. I can also apparently add the hideous:
<meta http-equiv="X-UA-Compatible" content="IE=9">
to my output but I'm not entirely certain what other effects that may have.
I'd like to understand first why IE10 is adding these nodes whereas IE9/FF/IE10compat aren't. There are some discussions I've found stating that whitespace in the HTML may be causing it but it seems to me that this shouldn't result in random nodes being created and, in any case, I don't think I have any superfluous white space. Although I should mention that the value of tableNode.data mentioned above as type string is actually \n, meaning that the newline at the end of the line may be creating this DOM entry.
But, to be honest, that seems ludicrous. HTML is supposed to ignore whitespace outside of tags as far as I'm aware, or at least fold it into a single element. I find it hard to believe that something like:
<tag>line 1</tag>
<tag>line 2</tag>
would result in three DOM entries, tag, empty node and tag just because there's a newline between them.
Any ideas on how to best solve this? Am I going to have to modify my Javascript code to skip these DOM entries?
You can never know where a browser might insert text nodes so you have to make sure you're getting the first child "element"in case the browser put a text node there.
Here's a simple function that will do that:
getFirstChildElement(parent) {
var node = parent.firstChild;
// advance until we get to an element node (skipping text and comment nodes)
while (node && node.nodeType !== 1) {
node = node.nextSibling;
}
return node;
}
Or, if you just want to get the <thead> element, you can simply use this:
table.getElementsByTagName("thead")[0]
Are you absolutely sure Firefox doesn't show those empty text nodes? I'm asking because it should, if it doesn't then it's a bug in Firefox.
Previously only IE behaved the way you expected. All other browsers including Firefox, Chrome, Safari and Opera followed W3C DOM standards which requires them to retain those whitespace. IE10 now join the ranks of other web browsers and behave in a standards compliant way.
You'd be right to complain that this doesn't make sense but it's what the standards require.
As such, the correct way to get the element is to check it's tagName:
var table = document.getElementById("table");
var child = table.firstChild;
while (child && child.tagName != 'thread') {
child = child.nextSibling;
}
// remember to check child after this point because it may be undefined
Additional explanation
Firebug and Chrome's DOM explorer hides those text elements as a matter of convenience, but it's still there. You can try this out:
<html>
<body>
<div id="foo">
<div id="bar">
</div>
</div>
<script>
var f = document.getElementById('foo');
document.body.innerHTML += f.firstChild.id + <br>;
document.body.innerHTML += f.firstChild.nextSibling.id + <br>;
</script>
</body>
</html>
In all browsers except older versions of IE the above page would output:
undefined
bar
That's because the firstChild is the empty text node. You can console.log it if you like to check out that firstChild.

Adding Values to a DOM element after the element has been created

I'm looking over some pretty extensive code for a web page that is generated from XML/XSL and then has Javascript generate the layout on the fly. What I am having troubles with is that it seems IE (v.10 specifically) is showing that elements are Empty Text Nodes if there is no value, and then regular text (no editable field) if there is a value.
This seems to change the behavior of to be just straight un-editable text.
From what I can see, the first step is for the Javascript to generate elements via the DOM
input = document.createElement("input");
input.setAttribute("id", "blah");
input.setAttribute("type", "text");
Then it will append it to the parent.
Then what seems to happen is that a function is executed that runs through the page again and inserts any values that these fields have.
input.value = "Hello World";
Chrome and Firefox will display the input fields properly with their fields populated, but IE10 will only show the value as if it was just plain text.
I've never worked with this sort of web page generation and hoping someone might be able to help me figure this out so I can troubleshoot this. Changing the way this works (at the time) is not an option so I'm trying to correct it so that IE is happy too.
Thanks
This specific code sequence works in all browsers I've tried it in (Chrome, IE, Firefox):
var elem = document.createElement("input");
elem.type = "text";
elem.id = "test";
document.body.appendChild(elem);
elem.value = "This is some text";
If your exact code is deviating from this, then you should examine the differences or post your exact sequence of code that demonstrates the problem in IE so we have a better idea how to debug or advise.
You can see a working demo of this in any browser you want to try it in here: http://jsfiddle.net/jfriend00/z2FpP/
Things to watch out for in your code:
Is any code setting .innerHTML of a parent which could be wiping out or resetting the child elements.
Is any code setting .innerHTML of the input field itself. This should not be done. Code should use .value to set the text of the input field.
Are there any code errors when the code runs in IE? Check the error console or debug console (hit F12 in IE to get to the debugger where you can see the error console).
Are there any other attributes being set in the input field that might make it read-only instead of editable?

How do I reset the value of a text input when the page reloads?

Firefox (and probably other browsers) want to keep whatever text the user entered in the text input, even after a reload. Just including the default text (that I want the input to revert to) in the html doesn't work:
<input tyep='text' value='default text' />
And neither does trying to use JS
window.onload = function() {document.getElementById("mytextinput").value = 'default text'}
You can use plain old HTML :)
Set autocomplete='off' in the attribute
<input type='text' value='default text' autocomplete='off' />
This works on most modern browsers.
Technically, you don't have to run a function onload to clear it--you could just put the javascript right in the page. For instance:
document.getElementById("mytextinput").value = ''
or with jQuery
$("mytextinput").val('');
Note that it's always a good idea to work with a dom listener to ensure that your javascript fires after the dom has been properly built. So, in the example of jQuery, this would be as easy as
$(document).ready(function() {
$("mytextinput").val('');
});
Try this:
<input type='text' id='mytextinput' value='default text' />
<script>
document.getElementById("mytextinput").value = 'default text';
</script>
This code should run as soon as the textbox has loaded. The onload event can seem inconsistent, I'm not entirely sure how it behaves in multiple browsers on different actions like refreshing.
You can call the script differently, put it in a function, put it somewhere else etc, but it's good to start with a working version.
If this doesn't work on other browsers there isn't much you can do because some browsers will try and be 'helpful' and always autofill for you.
I think you should do a server side coding on page reload. Page reload is generally a postback type idea . The page gets reloaded from server. If you are doing asp.net , in page_load method add your default text. :)

Javascript error: [elementname] has no properties

I'm doing some maintenance coding on a webapp and I am getting a javascript error of the form: "[elementname] has no properties"
Part of the code is being generated on the fly with an AJAX call that changes innerHTML for part of the page, after this is finished I need to copy a piece of data from a hidden input field to a visible input field.
So we have the destination field: <input id="dest" name="dest" value="0">
And the source field: <input id="source" name="source" value="1">
Now when the ajax runs it overwrites the innerHTML of the div that source is in, so the source field now reads: <input id="source" name="source" value="2">
Ok after the javascript line that copies the ajax data to innerHTML the next line is:
document.getElementById('dest').value = document.getElementById('source').value;
I get the following error: Error: document.getElementById("source") has no properties
(I also tried document.formname.source and document.formname.dest and same problem)
What am I missing?
Note1: The page is fully loaded and the element exists. The ajax call only happens after a user action and replaces the html section that the element is in.
Note2: As for not using innerHTML, this is how the codebase was given to me, and in order to remove it I would need to rewrite all the ajax calls, which is not in the scope of the current maintenance cycle.
Note3: the innerHTML is updated with the new data, a whole table with data and formatting is being copied, I am trying to add a boolean to the end of this big chunk, instead of creating a whole new ajax call for one boolean. It looks like that is what I will have to do... as my hack on the end then copy method is not working.
Extra pair of eyes FTW.
Yeah I had a couple guys take a look here at work and they found my simple typing mistake... I swear I had those right to begin with, but hey we live and learn...
Thanks for the help guys.
"[elementname] has no properties" is javascript error speak for "the element you tried to reference doesn't exist or is nil"
This means you've got one or more of a few possible problems:
Your page hasn't rendered yet and you're trying to reference it before it exists
You've got a spelling error
You've named your id the same as a reserved word (submit on a submit button for instance)
What you think you're referencing you're really not (a passed variable that isn't what you think you're passing)
Make sure your code runs AFTER the page fully loads. If your code runs before the element you are looking for is rendered, this type of error will occur.
What your describing is this functionality:
<div id="test2">
<input id="source" value="0" />
</div>
<input id="dest" value="1" />
<script type="text/javascript" charset="utf-8">
//<![CDATA[
function pageLoad()
{
var container = document.getElementById('test2');
container.innerHTML = "<input id='source' value='2' />";
var source = document.getElementById('source');
var dest = document.getElementById('dest');
dest.value = source.value;
}
//]]>
</script>
This works in common browsers (I checked in IE, Firefox and Safari); are you using some other browser or are you sure that it created the elements correct on innerHTML action?
It sounds like the DOM isn't being updated with the new elements to me.
For that matter, why are you rewriting the entire div just to change the source input? Wouldn't it be just as easy to change source's value directly?
This is a stretch, but just may be the trick - I have seen this before and this hack actually worked.
So, you said:
Ok after the javascript line that copies the ajax data to innerHTML the next line is:
document.getElementById('dest').value = document.getElementById('source').value;
Change that line to this:
setTimeout(function() {
document.getElementById("dest").value = document.getElementById("source").value;
}, 10);
You really shouldn't need this, but it is possible that the time between your setting the innerHTML and then trying to access the "source" element is so fast that the browser is unable to find it. I know, sounds completely whack, but I have seen browsers do this in certain instances for some reason that is beyond me.
Generally you shouldn't use innerHTML, but create elements using DOM-methods. I cannot say if this is your problem.

Categories

Resources