Problem using dynamically added html-object from javascript - javascript

I have a problem with dynamically including an object-tag in my html.
We have a external service which we call to get some html-fragment, it includes an object-tag, a script and a simple html-form. I take that content and add it to a div in my page and then try to execute the script that uses the included object. When i debug using Firebug I can see that the code is correctly inserted in the page but the script gets an error when it tries to access the object. It seems to me that the object isn’t initialized. Let me show you some code to exemplify what I mean.
getFragment makes an ajax call using jQuery to get the content.
var htmlSnippet = RequestModule.getFragment( dto );
$('#plugin').html( htmlSnippet ).hide();
The included content in plugin-div looks like this
<div id="plugin" style="display: none; ">
Browser:
Chrome
<object name="signer" id="signer" type="application/x-personal-signer2"></object>
<form method="POST" name="signerData" action="#success">
<input name="nonce" value="ASyhs..." type="hidden">
<input name="signature" value="" type="hidden">
<input name="encodedTbs" value="U2l..." type="hidden">
<input name="provider" value="nexus-personal_4X" type="hidden">
<input type="submit" onclick="doSign()" value="Sign">
</form>
</div>
The javascript that tries to use the “signer” object looks like this:
function doSign(){
var signer2 = document.getElementById("signer");
retVal = signer2.SetParam('TextToBeSigned', 'some value...');
... and then some more
}
It’s when i call the signer2.SetParam method that I get an error saying
Object #<an HTMLObjectElement> has no method 'SetParam'
But when I use the original page where the content is loaded when the page loads the script works so I know that the ‘SetParam’ method exists on the object and that the script works. But somehow it doesn’t work when I dynamically add it to the page afterwards.
I’ve Googled this a lot the last couple of days with no luck.
Does anyone have any idea on how to get this to work?
Best regards,
Henrik

First of all Object tag is not fully supported in all browsers (Source)
Next, from my experience, jQuery (which heavily relies on document.createDocumentFragment) sometimes fails to attach/trigger events on dynamically created/cloned DOM nodes, which could explain why your object failed to initialize.
That said, to try and fix your problem, I suggest using native document.createElement and document.appendChild methods instead of jQuery.html. You can try document.innerHTML but if that fails, you can always go with the ones I mentioned earlier.
My suggestion is to either alter your service to replace:
<script type="text/javascript">
function addElement(parentid, tag, attributes) {
var el = document.createElement(tag);
// Add attributes
if (typeof attributes != 'undefined') {
for (var a in attributes) {
el.setAttribute(a, attributes[a]);
}
}
// Append element to parent
document.getElementById(parentid).appendChild(el);
}
addElement('plugin', 'object', {name:"signer",id:"signer",type:"application/x-personal-signer2"});
</script>
OR if you cannot change the content that is returned by the service, run this after you include the content onto your page:
<script type="text/javascript">
/*
* Goes through al the object tags in the element with the containerid id
* and tries to re-create them using the DOM builtin methods
*/
function reattachObjectTags(containerid) {
jQuery('#'+containerid+' object').each(function(){
var attrs = {}, el = this;
// We're insterested in preserving all the attributes
var saved_attrs = {}, attr;
for(var i=0; i < el.attributes.length; i++) {
attr = el.attributes.item(i);
if(attr.specified) {
saved_attrs[attr.nodeName]=attr.nodeValue;
}
}
this.parentNode.removeChild(this);
var new_element = document.createElement('object');
for (var a in saved_attrs) {
new_element.setAttribute(a,saved_attrs[a]);
}
document.getElementById(containerid).appendChild(new_element);
});
}
// Do your stuff
var htmlSnippet = RequestModule.getFragment( dto );
$('#plugin').html( htmlSnippet ).hide();
// reattach all the object elements in #plugin
reattachObjectTags('plugin');
</script>
THIS IS ALL UNTESTED -
I typed this off the top of my mind, since I don't have the means to fire up IE and test this.

For a jQuery solution, I think this should work:
$("input:submit").click(function(){
$("#signer").append('<param name="TextToBeSigned" value="some value ...">');
... and then some more
});
Might want to give the submit button a class or an id and use that as a selector, if you have multiple forms on that page though.
Hope this helps.

I've set up a test script here: https://dl.dropbox.com/u/74874/test_scripts/object.html
If you open up Firebug/Web Inspector, you'll see that the SetParam method is in-fact, not defined. I don't know what it's supposed to do, but it's not defined in either case. If you're trying to add <param> tags to your embed, you could use the DOM API to do that. There is some code in the test script that does that, but I'll paste it here anyway:
var obj_signer = document.getElementById('signer');
var obj_p = document.createElement('param');
obj_p.id = "myp2";
obj_p.name = "TextToBeSigned";
obj_p.value = "some value ...";
obj_p.setAttribute('valueType', 'ref');
obj_signer.appendChild(e);
Or be faster using jQuery:
$("#signer").append("<param id='myp2' name='TextToBeSigned' value='some value ...' valueType='ref'></param>");

Related

show all the values with .html [duplicate]

Lets say I have an empty div:
<div id='myDiv'></div>
Is this:
$('#myDiv').html("<div id='mySecondDiv'></div>");
The same as:
var mySecondDiv=$("<div id='mySecondDiv'></div>");
$('#myDiv').append(mySecondDiv);
Whenever you pass a string of HTML to any of jQuery's methods, this is what happens:
A temporary element is created, let's call it x. x's innerHTML is set to the string of HTML that you've passed. Then jQuery will transfer each of the produced nodes (that is, x's childNodes) over to a newly created document fragment, which it will then cache for next time. It will then return the fragment's childNodes as a fresh DOM collection.
Note that it's actually a lot more complicated than that, as jQuery does a bunch of cross-browser checks and various other optimisations. E.g. if you pass just <div></div> to jQuery(), jQuery will take a shortcut and simply do document.createElement('div').
EDIT: To see the sheer quantity of checks that jQuery performs, have a look here, here and here.
innerHTML is generally the faster approach, although don't let that govern what you do all the time. jQuery's approach isn't quite as simple as element.innerHTML = ... -- as I mentioned, there are a bunch of checks and optimisations occurring.
The correct technique depends heavily on the situation. If you want to create a large number of identical elements, then the last thing you want to do is create a massive loop, creating a new jQuery object on every iteration. E.g. the quickest way to create 100 divs with jQuery:
jQuery(Array(101).join('<div></div>'));
There are also issues of readability and maintenance to take into account.
This:
$('<div id="' + someID + '" class="foobar">' + content + '</div>');
... is a lot harder to maintain than this:
$('<div/>', {
id: someID,
className: 'foobar',
html: content
});
They are not the same. The first one replaces the HTML without creating another jQuery object first. The second creates an additional jQuery wrapper for the second div, then appends it to the first.
One jQuery Wrapper (per example):
$("#myDiv").html('<div id="mySecondDiv"></div>');
$("#myDiv").append('<div id="mySecondDiv"></div>');
Two jQuery Wrappers (per example):
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').html(mySecondDiv);
var mySecondDiv=$('<div id="mySecondDiv"></div>');
$('#myDiv').append(mySecondDiv);
You have a few different use cases going on. If you want to replace the content, .html is a great call since its the equivalent of innerHTML = "...". However, if you just want to append content, the extra $() wrapper set is unneeded.
Only use two wrappers if you need to manipulate the added div later on. Even in that case, you still might only need to use one:
var mySecondDiv = $("<div id='mySecondDiv'></div>").appendTo("#myDiv");
// other code here
mySecondDiv.hide();
if by .add you mean .append, then the result is the same if #myDiv is empty.
is the performance the same? dont know.
.html(x) ends up doing the same thing as .empty().append(x)
Well, .html() uses .innerHTML which is faster than DOM creation.
.html() will replace everything.
.append() will just append at the end.
You can get the second method to achieve the same effect by:
var mySecondDiv = $('<div></div>');
$(mySecondDiv).find('div').attr('id', 'mySecondDiv');
$('#myDiv').append(mySecondDiv);
Luca mentioned that html() just inserts hte HTML which results in faster performance.
In some occassions though, you would opt for the second option, consider:
// Clumsy string concat, error prone
$('#myDiv').html("<div style='width:'" + myWidth + "'px'>Lorem ipsum</div>");
// Isn't this a lot cleaner? (though longer)
var newDiv = $('<div></div>');
$(newDiv).find('div').css('width', myWidth);
$('#myDiv').append(newDiv);
Other than the given answers, in the case that you have something like this:
<div id="test">
<input type="file" name="file0" onchange="changed()">
</div>
<script type="text/javascript">
var isAllowed = true;
function changed()
{
if (isAllowed)
{
var tmpHTML = $('#test').html();
tmpHTML += "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').html(tmpHTML);
isAllowed = false;
}
}
</script>
meaning that you want to automatically add one more file upload if any files were uploaded, the mentioned code will not work, because after the file is uploaded, the first file-upload element will be recreated and therefore the uploaded file will be wiped from it. You should use .append() instead:
function changed()
{
if (isAllowed)
{
var tmpHTML = "<input type=\"file\" name=\"file1\" onchange=\"changed()\">";
$('#test').append(tmpHTML);
isAllowed = false;
}
}
This has happened to me . Jquery version : 3.3.
If you are looping through a list of objects, and want to add each object as a child of some parent dom element, then .html and .append will behave very different. .html will end up adding only the last object to the parent element, whereas .append will add all the list objects as children of the parent element.

jqueryui: "TypeError: this.menu.element is undefined"

I'm trying to make an jquery-ui autocomplete widget out of an input created in JavaScript, like so:
function drawAutocomplete() {
var fooBox = document.createElement("input");
fooBox.id = "foobox";
fooBox.name = "foobox";
window.document.firstChild.appendChild(fooBox);
$("#foobox").autocomplete({source: ["eenie", "meenie"]});
}
but I keep getting a
TypeError: this.menu.element is undefined
whenever I try to interact with the box, and no autocompletion alternatives are displayed.
Is it not possible to use dynamically created items in this way? What else could I have misunderstood?
Your element isn't added to the page properly:
window.document.firstChild.appendChild(fooBox);
Will try to append the data to the doctype (or HTML) tag.
Use
window.document.body.appendChild(fooBox);
So your dynamic code is fine (a strange blend of plain JS and jQuery), but should work fine once you add the input to the correct element. #phillip100's answer shows you a nice way to optimize.
Demo:
$(document).ready(
function drawAutocomplete() {
var fooBox = document.createElement("input");
fooBox.id = "foobox";
fooBox.name = "foobox";
document.body.appendChild(fooBox);
$("#foobox").autocomplete({source: ["eenie", "meenie"]});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/themes/smoothness/jquery-ui.css" />
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.3/jquery-ui.min.js"></script>
Simple way using jQuery:
function drawAutocomplete() {
// used body, since I'm not sure what element you're reffering to
$('<input type="text" id="foobox" name="foobox">').appendTo('body')
// don't search the DOM for '#foobox', use created element:
.autocomplete({source: ["eenie", "meenie"]});
}
DEMO
Or using your code, wrap the fooBox HTML element into jQuery object.
function drawAutocomplete() {
var fooBox = document.createElement("input");
fooBox.id = "foobox";
fooBox.name = "foobox";
// used body, since I'm not sure what element you're reffering to
document.body.appendChild(fooBox);
$(fooBox).autocomplete({source: ["eenie", "meenie"]});
}
Either way, you don't have to search the DOM for '#foobox', since you have already cached the element here: var fooBox(second example) or $('<input ...>')(first example).
DEMO

Getting DOM element information from javascript

So I'm trying to grab some attributes of a div table from the DOM from within a script - and failing.
Code should speak for itself:
<head>
<script type = "text/javascript" src = "SCRIPT_IN_QUESTION.js"></script>
</head>
<body>
<div id = "my_elements">
<!-- I want to access 'attribute-one' data attributes from
all children -->
<div data-attribute-one = "Hello"></div>
<div data-attribute-one = "World"></div>
</div>
</body>
I've seen how this is done with an embedded script in the DOM using something along the lines of:
<script type = "text/javascript">
var values = document.getElementById('my_elements').childNodes;
var foo = values[0].getAttribute('data-attribute-one');
</script>
I'm trying to access these attributes from within a script that isn't embedded into the DOM, and getAttribute is undefined - guess you can only ue that within the DOM.
How do I access this data in a script 'outside' the DOM?
As the below comment states, doing this after the DOM has been loaded makes sense to do, and I'm doing it something like:
window.addEventListener('load', onload, false);
function onload(){
var data = document.getElementById('canvas_segments').childNodes;
//This throws an undefined error
var attribute = data[0].getAttribute('data-attribute-one');
}
Many thanks
Use .children instead of .childNodes (which also includes text nodes). And you may need to wrap your call into onload event, e.g.
<body onload="onLoad()";>
<div id = "my_elements">
<!-- I want to access 'attribute-one' data attributes from
all children -->
<div data-attribute-one = "Hello"></div>
<div data-attribute-one = "World"></div>
</div>
</body>
and in your script:
function onLoad() {
var values = document.getElementById('my_elements').children;
var foo = values[0].getAttribute('data-attribute-one');
}
You have a timing issue here. The script in question is being executed before the DOM is complete, so there is no element.
Either you can move the script tag to the bottom of the page, after all other markup, or you can put the code in a window.onload function.
First of all in the code snippet above, you are attaching the javascript before the DOM structure is loaded. Attach the js file after body so that the HTML content gets loaded first and then the js executes.
After you have done this you will still hit an issue saying, 'Uncaught TypeError: undefined is not a function.' This will be because you are using 'ChildNodes' instead of 'Children.' The difference between the two is that Children gets only Elements whereas 'ChildNodes' will fetch nodes too. Refer this question for a clearer distinction between the two (What is the difference between children and childNodes in JavaScript?)
You might be interested in a working copy of your code: http://goo.gl/HonxtJ

How to fix "Uncaught TypeError: Cannot call method 'appendChild' of null"

I am attempting to grab text from a HTML text area, and call the create() method when a 'Submit' button is pressed. The method is trying to use the message from the text area, and post that to its own p tag with class, and post a date stamp in its own p tag, and its own class.
These will both be in the div 'comments'. The error I am getting (using developer tools in Chrome), is
Uncaught TypeError: Cannot call method 'appendChild' of null.
This is aimed at "cmt.appendChild(divTag);". I am very new to Javascript, and this is just practise for me to increase my skills. All help is greatly appreciated!
var cmt = document.getElementById('comments');
function create() {
var username = 'User',
message = document.getElementById("textBox").value,
divTag = document.createElement('div'),
p1 = document.createElement('p'),
p2 = document.createElement('p');
divTag.className = 'comment';
p1.className = 'date';
p1.innerHTML = new Date();
divTag.appendChild(p1);
p2.className = 'message';
p2.innerHTML = username + ': ' +message;
divTag.appendChild(p2);
cmt.appendChild(divTag);
}
The error you are getting suggests that there is no element with the ID "comments" on your page. document.getElementById will return null when no element with such an ID is found, and thus cmd.appendChild(divTag) will be executed as null.appendChild(divTag).
If you are certain that the element exists, you may be executing your JavaScript that assigns the cmt variable before that element is created by the browser. To prevent that, standard practice is to place the <script> tag which includes your external JavaScript just before the closing </body> tag.
If you can't move your script tag for some reason, try running the code that assigns the variable with $(document).ready() (jQuery) or equivalent.
Your code works if the required elements exist. As you can see in this Fiddle.
Works if HTML is similar to:
<div id="comments">
<input id="textBox" type="textBox" value="Hello" />
</div>
My guess is that one of the identifiers might be misspelled or the element as you expect it does not exist.
However, if you are running the script in an external file it might try to execute before the document is fully loaded, hence your script is referring to elements not yet ready in the DOM.
In jQuery you would wrap a $(document).ready(function(){// your code here..}) around.
There is some details on how to do this in just JavaScript in this SO post: Documen Ready equivalent without jQuery.
Actually, his code is fine. Its just that JavaScript runs BEFORE HTML. A simple fix is to nest all of your code inside of a function with any name. Then use window.onload to run the function after the HTML is loaded. so basically:
window.onload = function any_name()
{
//code to be executed
}
This is useful especially if you do not want to move your script tag. I generally like to leave the tag where it is.

Why does appendChild only work when I remove the docType

When I put any sort of doctype declaration like <!DOCTYPE html >, appendChild does not work.... Why?
<form>
<script language="javascript">
function function2() {
var myElement = document.createElement('<div style="width:600; height:200;background-color:blue;">www.java2s.com</div>');
document.forms[0].appendChild(myElement);
}
</script>
<button onclick="function2();"></button>
</form>
I'm trying to get data from a popup window's parent opener...is that possible? The data can be a string literal or value tied to the DOM using jQuery .data()
If you're having this problem in IE, it's probably because the presence of a DOCTYPE declaration forces the browser into "standards-compliance" mode. This can cause code that doesn't conform to expected standards to break.
In your case, it's probably because document.createElement doesn't accept an HTML fragment - it accepts an element name, e.g. document.createElement('div').
Try replacing your function body with something like this:
var myElement = document.createElement('div');
myElement.style.width = '600px';
myElement.style.height = '200px';
myElement.style.backgroundColor = 'blue';
myElement.appendChild(document.createTextNode('www.java2s.com'));
document.forms[0].appendChild(myElement);
Read up on the document object model here: https://developer.mozilla.org/en/DOM
Also, jQuery is good for easily creating elements using the syntax you specified.

Categories

Resources