getElementById of something inside a function, but from outside - javascript

I keep learning very simple things about functions, returns, id and everything, so I ran into another problem that looks simple, but I cannot understand why it happens. Check this code:
function test() {
var text = document.createTextNode("Hello");
text.id = "t";
}
var whatIjustwrote = window.document.getElementById("t");
alert(whatIjustwrote);​
Does the getElementById has restrictions to look only for global items? What would be the way to make that alert output the text node inside the function?
Thank you for any comment. The last few days asking things here I have been learning quite a lot!
JSFiddle

Firstly, getElementById will only return an element, and you're creating a text node.
Secondly, it will only return an element that has been added to the DOM. The node you create doesn't get added to the DOM, so it wouldn't be found even if it could be.
Finally, you don't actually call the test function, so the text node isn't even created in memory.
Here's an updated fiddle that demonstrates getElementById actually working:
function test() {
var text = document.createElement("span"); //Create an element
text.innerHTML = "Hello";
text.id = "t";
document.body.appendChild(text); //Add it to the DOM
}
test(); //Invoke the function (so the element actually gets created)
var yourElement = document.getElementById("t"); //Get reference to element

getElementById does only search for element nodes. You did create a text node, which has neither attributes nor an id - you just added a custom property to the JS object. Also, you did not append your node to the document, so it couldn't have been found in the DOM tree.
You might want to read an introduction to the DOM (at MDN), the introduction at quirksmode.org or even the W3 standard itself (especially the introduction section)
function test() {
var elem = document.createElement("span"); // Create an element
var text = document.createTextNode("Hello"); // Create a textnode
elem.appendChild(text); // add text to the element
elem.id = "t"; // assign id to the element
document.body.appendChild(elem); // Add it to the DOM
}
test();
var yourElement = document.getElementById("t"); // Get the element from the DOM
alert(yourElement.textContent); // alerts "Hello"
// you also could have alerted yourElement.firstChild.data - the content of the
// textnode, but only if you had known that yourelement really has a firstchild
(Demo at jsfiddle.net)

A couple of points that come to mind..
1) You cant give a textNode an id attribute (you're actually giving it a new member variable named id)
2) To find an element it must exist in the document's DOM
Do this instead:
var mSpan = document.createElement('span');
mSpan.id = 't';
mSpan.appendChild( document.createTextNode('Hello') );
document.body.appendChild(mSpan);
var whatIjustwrote = window.document.getElementById("t");
alert(whatIjustwrote.innerText);

Does the getElementById has restrictions to look only for global
items?
The answer is no. First you have to define global items anyways. Anything that is attached to the DOM is in fact global, and in terms of global javascript objects there is only one, window, in the case of a browser. You are creating a function but you're never executing it.
In addition the text node cannot actually have an id or any other attribute. You need an element for this, so even if you execute the function you would still get null. Also creating a node does not attach is to the DOM, so you won't be able to access it even if this isn't a text node.
I have updated your fiddle.

Related

The reasoning behind direct link between DOM Element and the variable it is assigned?

I am kinda newbie and I really want to understand why the variables we create don't just get the values inside the DOM element, but becomes the DOM element?
Here is some basic code;
var elementValues = document.getElementById("p1");
elementValues.style.color = "red";
// I don't understand why we don't need this step in order to manipulate the DOM.
// document.getElementById.getElementById("p1") = elementValues;
Aren't we basically saying to copy the values from DOM element with an id of p1 and paste them into elementValues?
But why the color of DOM element changes when I change the color of elementValues? From what I understand it acts like a pointer.
In Javascript, Object variables store a reference to the objects. Hence, document.getElementById returns a reference. So when you modify the values of elementsValues, your are editing the referenced object.
Have a look on Working with Objects - Comparing Object. You could read the whole page also to have an overview.
Yes, it is like a pointer.
By using var elementValues = document.getElementById("p1"); you are assigning a reference to the DOM element to the variable. Nothing about the element gets saved to the variable, but "where to find it".

Alternative to always referencing to parents and children to modify DOM when using 'this'

I notice that I'm always using this for everything, when I need to share behaviour between components.
Example
I was creating a customised file input component, and I had to return the file that was added by the user with JS therefore. But there were three inputs, and I need to create a function that would be shared among them, so it could not be specific nor unique. It should work for all of'em.
var setFilename = function () {
// get element I want
var input = this
var filepath = input.value
var m = filepath.match(/([^\/\\]+)$/)
var filename = m[1]
var spanLabel = input.parentNode.querySelector('.input')
// apply to element I wanted to get
spanLabel.innerHTML = filename;
}
The problem
Notice the had work just to reach the element I want in a general way. I used parentNode.
I'm always doing that, and there are even codes that finishes like that:
var element = this,
elementParent = this.parentNode,
elementIWant = selectedSpan.parentNode.querySelector('.class');
I dont see it like a good practice. Is it? I don't see it in other developers' code. I did it with jQuery (parent(), closest() etc) and JS.
Is there another approach?
How can I do abstract functions for manipulating DOM the right way? Is that the way people do?
The logic to get a value from an input field and put that value into a target element must assume some kind of DOM structure exists (as in your example) if the target element is not explicitly known.
A convention that I often use is adding a data- (data dash) attribute to the input field to store the id of the target element that will be updated with the input value. For example:
<div>
<label for="filename">Filename: </label>
<input type="text" id="filename" data-target-id="filename-display" />
</div>
<div class="some-displayed-values">
<span class="input" id="filename-display"></span>
</div>
Then, the target element can be selected by its ID value, like so:
function setFilename () {
var input = this;
// older browsers might not have input.dataset available
var targetId = input.dataset.targetId || input.getAttribute('data-target-id');
var target = document.getElementById(targetId);
target.innerHTML = input.value;
}
Example on jsfiddle: http://jsfiddle.net/silkster/ex1dvv0s/
If you need to access the parent, parentNode or parentElement are the right ways.
If other developers don't use them, it may be because they don't want to access the parent.
However, be aware that
parentNode may return a node instead of an element. So using properties or methods only available to elements on the returned value may throw. If you expect the result to be an element, use parentElement instead.
Both parentNode and parentElement may return null. So it may be a good idea to check the returned value before attempting to access its properties.

WinJS won't allow me to attach event handlers to dynamically created HTML elements

I first tried the easy yet disapproved way of creating elements with innerHTML:
// "Alarms" is a div that will contain the new divs
document.getElementById("Alarms").innerHTML += "<div onclick=\"watevr\">";
But Visual Studio told me I'm not allowed to insert JavaScript like that, then I tried the right/other way:
var alarms = document.getElementById("Alarms");
var div = document.createElement("div");
div.innerHTML = "I'm the new div!";
div.onclick = "watevr";
alarms.appendChild(div);
Now whenever I run my code I get this error:
0x800a138f - JavaScript runtime error: Unable to set property 'innerHTML' of undefined or null reference
and the code breaks at alarms.appendChild(div);
I've tried div.addEventListener("click", function(){watevr}, false); instead of onclick to no avail.
Interestingly, removing div.onclick = "watevr"; fixes the problem completely.
What am I doing wrong here?
First of all try to put all your code in a separate file then create a self execute fucntion like this:
(function(){
var MyApp {
init : function(){
this.renderMyDiv();
},
renderMyDiv: function(){
var alarms = document.getElementById("Alarms");
var element = document.createElement('div');
element.className = 'superDiv';
element.innerHTML = 'Im the div';
alarms.appendChild(element);
this.addDivEvent('superDiv');
}
addDivEvent: function(className){
var element = document.getElementsByClassName(className);
for(var i = 0, j=element.length; i<j; i++){
element[i].addEventListener("click", function(){
//TO-DO
}
}
}
}
MyApp.init();
})();
This is the old way to do it but you can also accomplish this vía jquery.
Problem 1 - You don't want to attach listeners via attributes anyway, so don't bother.
Problem 2 - You seem to have a bug causing getElementById to return null or undefined. From your text it looks like you're saying createElement returned null, but that shouldn't be possible so I'm assuming you just pasted the wrong code and/or error.
Problem 3 - You're using innerHTML in the second block to add non-HTML text.
Problem 4 - You're assigning a string to the onclick property (it should be a function).
Using createElement, appendChild, and either onclick or addEventListener is the right approach. Don't adding to innerHTML unless you really need to insert HTML. Since the string doesn't contain anything dynamic I don't see why it would mark the element as u trusted, but maybe it does... If that is the case, you can either:
A) Use innerText.
B) Create more elements directly or using a template or fragment.
C) Call toStaticHTML on the string before assigning to innerHTML.
4) Wrap the call that adds the element to the DOM in an MSApp.execUnsafeLocalFunction call.
But you'll need to fix your null dereference bugs first.

getElementById Where Element is dynamically created at runtime

I have created an object at runtime by using innerHTML tag, now I want to access this element by using getElementById, when I accessed the element its return NULL value. Kindly suggest me any direction so that I can acheive this,
Here is the following code hint which I am using
In HTML
<div id="web">
<object id="test"></object>
</div>
In JS
document.getElementById("web").innerHTML="<object id='test2'></object>";
.
.
var obj = document.getElementById("test2");
Here obj return null value.
Did you assign an id to the freshly created element? Did you insert the element into the document tree (using appendChild or insertBefore)? As long as the element is not inserted into the DOM, you can't retrieve it using document.getElementById.
Example of element creation:
var myDiv = document.createElement('div');
myDiv.id = 'myDiv';
document.body.appendChild(myDiv);
document.getElementById('myDiv').innerHTML = 'this should have worked...';
[edit] Given the later supplied code, a third question emerges: is your script located at the bottom of your html page (right before the closing </body> tag)? If it's in the header of the document, your scripting may be running before the document tree is completely rendered. If your script has to be in the header of the document, you could use a load handler to run it after rendering of the document:
window.onload = function(){
document.getElementById("web").innerHTML='<object id="test2"></object>';
// [...]
var obj = document.getElementById('test2');
};
To add an element using JavaScript, you need to do 2 things.
Create the element
var element = document.createElement(tagName);
Add it to the dom
document.body.insertBefore(selector, element);
or
document.getElementByID(selector).appendChild(element);
More info here: MDN
If a DOM node is dynamically created, then there's no need EVER to find it again with document.getElementById().
By creating the node in the right way, you can keep a reference to it as a javascript variable, which can be used anywhere within scope.
For example:
window.onload = function(){
var myDiv = document.createElement('div');
document.body.appendChild(myDiv);
//and now a function that's called in response to some future event
function whatever(){
...
myDiv.style.color = 'red';
...
}
};
Note: The inner function (called at some point(s) future) has access to the myDiv variable because the outer function forms a "closure", within which variables are kept alive and accessible, even though the outer function has completed and returned.

Use getElementById for elements that are not [yet] in the DOM?

As far as I know document.getElementById('myId') will only look for HTML elements that are already in the document. Let's say I've created a new element via JS, but that I haven't appended it yet to the document body, is there's a way I can access this element by its id like I would normally do with getElementById?
var newElement = document.createElement('div');
newElement.id = 'myId';
// Without doing: document.body.appendChild(newElement);
var elmt = document.getElementById('myId'); // won't work
Is there a workaround for that?
(I must tell that I don't want to store any reference to this particular element, that's why I need to access it via its Id)
Thank you!
If it isn't part of the document, then you can't grab it using document.getElementById. getElementById does a DOM lookup, so the element must be in the tree to be found. If you create a floating DOM element, it merely exists in memory, and isn't accessible from the DOM. It has to be added to the DOM to be visible.
If you need to reference the element later, simply pass the reference to another function--all objects in JavaScript are passed by reference, so working on that floating DOM element from within another function modifies the original, not a copy.
For anyone stumbling upon this issue in or after 2019, here is an updated answer.
The accepted answer from Andrew Noyes is correct in that document.getElementById won't work unless the element exists in the document and the above code already contains a reference to the desired element anyway.
However, if you can't otherwise retrieve a direct reference to your desired element, consider using selectors. Selectors allow you to retrieve nodes that aren't necessarily in the DOM by using their relationship to other nodes, for example:
var child = document.createElement("div");
child.id = "my_id";
var parent = document.createElement("div");
parent.appendChild(child);
var child2 = parent.querySelector("#my_id");
getElementById is a method on the document object. It's not going to return anything not in the document.
On not storing a reference, huh? If you could magically pull it out of the air by id, then the air would be a reference to it.
If you've created it, just pass the object to other functions and access it directly?
function createDiv()
{
var newElement = document.createElement('div');
doWorkWithDiv(newElement);
}
function doWorkWithDiv(element)
{
element.className = 'newElementCSS';
element.innerHTML = 'Text inside newElement';
addToDoc(element);
}
function addToDoc(element)
{
document.body.appendChild(element);
}

Categories