JavaScript Syntax for creating DOM nodes as variables - javascript

Firstly, I'm trying to create multiple DOM nodes and cache them as a variable for use in a function. What I want to do is create a function that sets-up the elements by classname. Then call that function as variable for use later.
Secondly, I'm not sure what the correct syntax is when manipulating inserted nodes via classname, when you want to select all classes with that name.
i.e for (var i = 0; i < insertedNodes.length; i++) {
To clarify what exactly I'm asking, my questions are this:
How to insert nodes as variables for use later on in function.
How to call those each of those variables.
How to call both of those variables together.
Hopefully my code will help explain what I'm trying to understand a little further:
var div1 = document.querySelector('.div1');
var div2 = document.querySelector('.div2');
var node1 = {};
var node2 = {};
var bothNodes = {};
function nodes() {
function insertNodes() {
node1 = div1.appendChild(nodeBase);
node2 = div2.appendChild(nodeBase);
bothNodes = [node1, node2];
}
function nodeBase() {
var node = document.createElement('div');
node.className = 'newNode';
}
function dosomething(node1, node2) {
//
}
function dosomethingElse(bothNodes) {
//
}
}
new nodes();

You don't return node here..
function nodeBase() {
var node = document.createElement('div');
node.className = 'newNode';
// ADD THIS LINE
return node;
}
And, as pointed out by Felix in the comment:
function insertNodes() {
node1 = div1.appendChild(nodeBase()); // Fixed
node2 = div2.appendChild(nodeBase()); // Fixed
bothNodes = [node1, node2];
}

This might get drowned with down votes, but pointing you to another direction.
How to insert nodes as variables for use later on in function.
you don't actually have to store those elements. You can always query them later.
How to call each of those variables.
You can select the first matching element with a particular class as follows:
var div1 = document.getElementsByClassName('.div1')[0];
OR
var div1 = document.querySelector('.div1');
How to call both of those variables together.
You can select all elements with a particular class, iterate over the collection and apply your logic as follows:
var nodes = document.getElementsByClassName('.div1');
OR
var nodes = document.querySelectorAll('.div1');
for(var i=0;i<nodes.length;i++){
// your loic
}

Related

converting JS array with objects to HTML collection

I got to the point with my project where I decided to simplify some of the js functions where I am looking for a parent in a DOM tree, then drill down to the elements many many times in one function. instead I though I will make instances of a function which will keep some data so then I can refer and operate on objects in easy way. I got it working but as I was going along, I decided to extend functionality and add some extra functions like getElementsByClassNameThenTagName.
I loop through the arrays and if add matching elements to the array.
I have noticed (sadly only now) that I am creating an array with elements rather than HTML collection. As a results, I cannot refer to the objects in my findings by typing buttons['reset'].disabled = false;. I can access my reset button by buttons[3].disabled = false; but this would cause a lot of inconvenience.
I am therefore looking for a way to convert my array with object into a HTML collection.
Please see below my current function:
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = [];
var regexStr = elementClass;
var regex = new RegExp(regexStr);
var x = moduleNode.getElementsByClassName(elementClass);
// console.log(x);
var y;
for ( var i = 0; i < x.length; i++ ) {
// console.log(i);
y = x[i].getElementsByTagName(elementTag);
// console.log(y);
for (var k=0; k<y.length; k++){
// console.log(y[k]);
results.push(y[k]);
}
// console.log(results);
}
return results;
};
Any suggestions please?
Thanks.
this.getElementsByClassNameThenTagName = function (elementClass, elementTag) {
if (parentNode == null) {
this.init();
}
var results = {}; // thid should be an object (collection)
var x = moduleNode.querySelectorAll("." + elementClass + " " + elementTag);
x.forEach(function(y) {
var name = y.getAttribute("name"); // if you really sure that all the matched elements have names
results[name] = y;
});
return results;
};
Now you can use the results array like this:
var someElement = results['some name'];
NOTE: All the matched elements x should have a name attribute, and all the name attributes of the matched elements should be unique.

Create a unique identifier for a DOM element

I am creating an object that stores various elements and their CSS properties.
The code I have now:
// My object
var cssStorage = {};
function store(element, cssProperty, value) {
// Initialize the (sub-)objects if they don't exist
cssStorage[element.id] = cssStorage[element] || {};
cssStorage[element.id][cssProperty] = cssStorage[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage[element.id][cssProperty] = value;
};
Example:
// My element
var box = document.getElementById("box");
// Let's call the function twice to save to properties
store(box, "display", "block");
store(box, "height", "74px");
Now my Object is populated like so:
cssStorage = {
box: { // <- box is the id of the HTML element <div id = "box"></div>
// The property-value pairs
display: "block",
height: "74px"
}
};
So now, if I type the code in the console:
return cssStorage.box.display; // Returns "block"
As you saw in the first block of code I posted, I used element.id as the element's unique identifier, to be able to use it as shown right above.
My problem is the dependency of my script upon element.id. Some elements of my DOM don't have an id and therefore the function is useless for these elements.
In essence, what I want to achieve is to call the function store when my element doesn't have an ID as follows:
// Some ways to get an element
var box = document.getElementsByClassName("boxes")[0];
var box = document.getElementsByTagName("div")[0];
var box = document.getElementsByName("jack")[0];
// It'll show an error, as the function uses 'element.id' and my element doesn't have one
store(box, "display", "block");
Is there a unique identifier for every node in the DOM?
Something that I could use as the name of:
cssStorage = {
[THE NAME]: {}
};
If not, how can I create a unique identifier for my elements, so that I can use the function as shown above without needing an id, class or other property that my element may not have?
You can easily coin a unique identifier for any element that doesn't yet have one:
var customIDprefix = "__myCustomPrefix__";
var customIDcntr = 0;
function getNextID() {
return customIDprefix + customIDCntr++;
}
And, then you can make sure any element you're using has a unique ID:
function checkID(elem) {
if (!elem.id) {
elem.id = getNextID();
}
}
If you're using ES6, you can also just use a WeakMap or Map object as your CSSStorage mechanism which let the DOM element itself be the key so you don't have to make a string key.
In that case, you'd just do this:
var cssStorage = new Map();
cssStorage[elem] = { // <- elem (your DOM element itself) becomes your key into the cssStorage
// The property-value pairs
display: "block",
height: "74px"
}
You could use an integer to handle a sequence and set the id to elements that does not have it, prefixing to avoid duplicates (for example 'myid' + idSequence++).
Please check if this works. Basically trying to clone the original element and assign it back to the original after adding id with random generator.
function store(element, cssProperty, value) {
if ( element.id == undefined ) {
var clonedElem = element.cloneNode(true);
clonedElem.id = Math.floor((Math.random() * 1000) + 1);
element = clonedElem;
}
// Initialize the (sub-)objects if they don't exist
cssStorage.[element.id] = cssStorage[element] || {};
cssStorage.[element.id][cssProperty] = cssStorage.[element][cssProperty] || {};
// Set the cssProperty to equal the value
cssStorage.[element.id][cssProperty] = value;
};

Update nested attributes in a JavaScript object

I want to change 'hello' to 'hey' programmatically, the solution should work with any number of nested elements (I just use 2 levels to keep it simple).
var data = {level1: {level2 : 'hello' }};
I have access to the 'data' variable, the path ('level1/level2') and the new value ('hey').
I tried to do:
var parents = 'level1/level2'.split('/');
var target = data;
for(var i=0; i<parents.length; i++){
target = data[parents[i]];
}
target = 'hey';
The idea was to travel to the root
target = data
then 1 level deep
target = data['level1']
...keep going
target = data['level1']['level2'] //data['level1'] === target
and modify the contents
target = 'hey'
But it looks like a lose the reference to the original object (data) when I do (target = target['level2']).
I guess I can build a string with the path and then evaluate it:
eval("data['level1']['level2']='hey');
Is there a better solution that dosen't involve eval()?
There are two issues. First is that you keep using data inside the loop, which means you're trying to access the top level keys instead of the inner keys. Change target = data[parents[i]]; to
target = target[parents[i]];
The second is that when you change the variable target, you're not changing the data variable but target instead. If you drop out of the loop one iteration earlier you can update the object which is stored as a reference:
for(var i=0; i<parents.length-1; i++){
target = target[parents[i]];
}
target[parents[i]] = 'hey';
Demo: http://jsfiddle.net/Lherp/
Try something like this:
var data = {level1: {level2 : 'hello' }};
var parents = 'level1/level2'.split('/');
var target = data;
for(var i=0; i < parents.length - 1; i++){
target = target[parents[i]];
}
target[parents[i]] = 'hey';
Or am I missing something?
edit: I was missing something (sorry, should have tested it first..)

How can I use multiple variables to retrieve JS objects?

I'm only working on my 3rd Javascript project, so this is probably easy to answer (at least I hope so).
I have learned to use JS object in place of arrays. In this project I have named multiple object with a nested system of IDs as follows:
animalia = new Object();
animalia.chordata = new Object();
animalia.chordata.actinopterygii = new Object();
animalia.chordata.actinopterygii.acipenseriformes = new Object();
etc.......
I'm having problems calling on objects named this way though. Here is my code:
function expand(event){
var target = event.target;
console.log(target);
var parent = target.parentNode;
console.log(parent);
var parentclass = parent.getAttribute("class");
console.log(parentclass);
if (parentclass == "kingdom"){
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentid = parent.getAttribute("id");
console.log(parentid);
----> var parentobj = window[parentid];
console.log(parentobj);}
else{
var upperclass = searchArray(parentclass);
console.log(upperclass);
var newdiv = document.createElement("div");
var newexpctrl = document.createElement("div");
var parentId = parent.getAttribute("id");
console.log(parentId);
var parentnode_ = document.getElementById(parentId);
console.log(parentnode_);
var gparentId = parentnode_.parentNode.id;
console.log(gparentId);
----> var parentobj = window[gparentId.parentId];
console.log(parentobj);
}
var childnumb = parentobj.children;
}
I am having my problem with the two statements indicated by "---->". In the first case, using a single variable works for pulling up the proper object. However, in the second case, using two variables, I fail to be able to access the proper object. What is the proper syntax for doing this? I have tried a plethora of different syntax combinations, but nothing seems to work correctly. Or should is there a better method for calling on JS objects other than using "window[variable]"?
P.S.- If you haven't figured it out by now, I am working on educational tools for use in learning biology. Thanks once again stackoverflow, you guys rule.
Assuming that the window object has something w/ the property matching a string that's the value of gparentId, you should be able to do:
var parentobj = window[gparentId][parentId];
The problem here is that the square bracket's notation is being applied to too much. gparentId is a string. It doesn't have a property called parentId. You therefore have to do this in two steps. First get:
window[gparentId]
Then get the appropriate property of that object
var parentobj = window[gparentId][parentId];
On a somewhat unrelated note, this isn't very well written JavaScript code:
Creating Objects
When creating new objects, always use the following syntax:
var obj = {};
That's what's generally been accepted as standard, so it's easier for people to read.
Declaring Variables in If Statements
You shouldn't really declare variables inside an if statement, especially when declaring the same variable in the else block, that's really confusing. Instead, declare all the variables at the top in a list and then use them without the var keyword lower down.
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj;
Note the commas instead of semi-colons which means I don't have to repeat the var keyword. Since the values of newdiv, newexpctrl and parentid are the same in either case, I give them their values straight away, making the contents of the if statement much shorter and easier to digest.
Result
function expand(event){
var target = event.target;
var parent = target.parentNode;
var parentclass = parent.getAttribute("class");
var newdiv = document.createElement("div"),
newexpctrl = document.createElement("div"),
parentid = parent.getAttribute("id"),
parentobj, upperclass;
if (parentclass == "kingdom"){
parentobj = window[parentid];
}else{
upperclass = searchArray(parentclass);
var _parentId = document.getElementById(parentId).parentNode.id;
parentobj = window[_parentId][parentId];
}
var childnumb = parentobj.children;
}
Note that I've left var _parentId inside the if since I think it probably improves readability, but you may choose to take it outside the if, since it will pollute the namespace of the function anyway.

trouble creating nested dom nodes in javascript

I've a function that takes an object as a parameter, and uses the structure of the object to create nested DOM nodes, but I receive the following error:
http://new.app/:75NOT_FOUND_ERR: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
What I would like my function to do, is, when supplied with a suitable object as a parameter, example:
var nodes = {
tweet: {
children: {
screen_name: {
tag: "h2"
},
text: {
tag: "p"
}
},
tag: "article"
}
};
It would create the following DOM nodes:
<article>
<h2></h2>
<p></p>
</article>
Here is my attempt so far:
function create(obj) {
for(i in obj){
var tmp = document.createElement(obj[i].tag);
if(obj[i].children) {
tmp.appendChild(create(obj[i].children)); /* error */
};
document.getElementById("tweets").appendChild(tmp);
};
};
I'm already struggling!
Ideally I'd like to eventually add more child key's to each object, not just tag, but also id, innerHTML, class etc.
Any hel would be much appreciated, though please: I'm sure a framework or library could do this for me in just a few lines of code, or something similar, but I'd prefer not to use one for this particular project.
If you could briefly explain your answers too it'd really help me learn how this all works, and where I went wrong!
Thank you!
NB: I've changed and marked the line in my function that the error message is talking about.
I changed it from:
mp.appendChild(obj[i].children);
to:
mp.appendChild(create(obj[i].children));
This is because I want any nested keys in the children object to also be created, so screen_name had a children key, they too would be created. Sorry, I hope you can understand this!
I'm looking at http://jsperf.com/create-nested-dom-structure for some pointers, this may help you too!
Your "create" function is going to have to be written recursively.
To create a node from your data (in general), you need to:
Find the "tag" property and create a new element
Give the element the "id" value of the element (taken from the data)
For each element in "children", make a node and append it
Thus:
function create(elementDescription) {
var nodes = [];
for (var n in elementDescription) {
if (!elementDescription.hasOwnProperty(n)) continue;
var elem = elementDescription[n];
var node = document.createElement(elem.tag);
node.id = n; // optional step
var cnodes = create(elem.children);
for (var c = 0; c < cnodes.length; ++c)
node.appendChild(cnodes[c]);
nodes.push(node);
}
return nodes;
}
That will return an array of document elements created from the original "specification" object. Thus from your example, you'd call:
var createdNodes = create(nodes);
and "createdNodes" would be an array of one element, an <article> tag with id "tweets". That element would have two children, an <h2> tag with id "screen_name" and a <p> tag with id "text". (Now that I think of it, you might want to skip the "id" assignment unless the node description has an explicit "id" entry, or something.)
Thus if you have a <div> in your page called "tweets" (to use your example, though if so you'd definitely want to cut out the "id" setting part of my function), you'd add the results like this:
var createdNodes = create(nodes), tweets = document.getElementById('tweets');
for (var eindex = 0; eindex < createdNodes.length; ++eindex)
tweets.appendChild(createdNodes[eindex]);
I added a function appendList that accepts a list of elements, and the container to append to. I removed the append to "tweets" part out of the create function to more effectively separate your code.
function create(obj) {
var els = [];
for(i in obj){
var tmp = document.createElement(obj[i].tag);
var children;
if(children = obj[i].children) {
var childEls = create(children);
appendList(childEls, tmp);
}
els.push(tmp);
};
return els;
};
function appendList(list, container){
for(var i = 0, el; el = list[i]; i++){
container.appendChild(el);
}
};
// gets an array of root elements populated with children
var els = create(nodes);
// appends the array to "tweets"
appendList(els, document.getElementById("tweets"));
Building on the previous answer:
I think you still need to create the element you're trying to append:
tmp.appendChild(children[prop].tag);
should be
tmp.appendChild(document.createElement(children[prop].tag));
function create(obj) {
for(i in obj){
var tmp = document.createElement(obj[i].tag);
var children;
if(children = obj[i].children) {
for(var prop in children)
tmp.appendChild(document.createElement(children[prop].tag));
}
document.getElementById("tweets").appendChild(tmp);
};
};

Categories