how to createElement with an id in one line - javascript

i know that i can create an element such as:
var foo = document.createElement('div');
and to set the div id i would do as such:
foo.setAttribute('id', 'divName');
and if you google long enough you will find someone doing code such as here
var google = createElement("a",{"href":"http://google.com"},"google"),
youtube = createElement("a",{"href":"http://youtube.com"},"youtube"),
facebook = createElement("a",{"href":"http://facebook.com"},"facebook"),
links_conteiner = createElement("div",{"id":"links"},[google,youtube,facebook]);
which would equal out to:
var foo = document.createElement('div', {'id':'divName);
yet when i run the above code the id or divName is not added and instead an empty div is created. so i am curious what i am doing wrong, and if it is even possible to both create and set the div name for an element using .createElement only?

If you want to add an object in one line, you can use:
Object.assign(document.createElement('div'),{id:"fd"})
here it is in action:
document.body.append(Object.assign(document.createElement('div'),{textContent:"fd"}));
no jquery needed.

Well, you can create your own prototype which will do it for you. Not the best solution, but will make you able to create element in one line and add attributes to it:
document.__proto__.customCreateElement = function(tag, attributes){
var e = document.createElement(tag);
for(var a in attributes) e.setAttribute(a, attributes[a]);
return e;
}
And then:
var a = document.customCreateElement('div', {id: "myId", name: "myName"});
results in:
<div id="myId" name="myName"></div>
Of course if you want to make it in pure JavaScript without any frameworks or libraries.

var createElement = function (tagName, id, attrs, events) {
attrs = Object.assign(attrs || {}, { id: id });
events = Object.assign(events || {});
var el = document.createElement(tagName);
Object.keys(attrs).forEach((key) => {
if (attrs [key] !== undefined) {
el.setAttribute(key, attrs [key]);
}
});
Object.keys(events).forEach((key) => {
if (typeof events [key] === 'function') {
el.addEventListener(key, events [key]);
}
});
return el;
}
createElement('div');

According to the spec , createElement accepts a second option object which only can have the is attribute. Unfortunately nothing for id, class style.. See also the MDN docs

If you can use jQuery you can write according to the doc:
$('<div/>', {id: 'divName'})
var ele = $('<div/>', {id: 'divName'});
console.log(ele.get(0).outerHTML);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Related

HTML element tag name from constructor

Consider the function:
tagName => document.createElement(tagName).constructor
which returns the constructor function of HTML elements with a particular tag name.
I would like to reverse this process and get the tag name (or names?) of an HTML element provided its constructor. I would like to do this for some unit tests that I'm writing, but there are probably other use cases.
A few examples:
tagNameOf(HTMLDivElement) // should be "div"
tagNameOf(HTMLIFrameElement) // should be "iframe"
tagNameOf(HTMLHtmlElement) // should be "html"
tagNameOf(HTMLTableRowElement) // should be "tr"
Unfortunately, I have no idea where to start from, except maybe using a static mapping.
Is there a simple method to achieve this?
I suppose a dirty-looking solution would be to check the constructor's toString():
const makeConstructor = tagName => document.createElement(tagName).constructor;
const constructorToTagName = constr => constr.toString().match(/HTML(\w+)Element/)[1].toLowerCase();
console.log(constructorToTagName(makeConstructor('div')));
console.log(constructorToTagName(makeConstructor('span')));
I extracted this solution of one of my projects:
const tagNameOf = (function () {
// This lookuptable is key to the solution, it must somehow be kept up to date
const elementNameLookupTable = {
'UList': ['ul'],
'TableCaption': ['caption'],
'TableCell': ['th', 'td'],
'TableCol': ['col', 'colgroup'],
'TableRow': ['tr'],
'TableSection': ['thead', 'tbody', 'tfoot'],
'Quote': ['q'],
'Paragraph': ['p'],
'OList': ['ol'],
'Mod': ['ins', 'del'],
'Media': ['video', 'audio'],
'Image': ['img'],
'Heading': ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'],
'Directory': ['dir'],
'DList': ['dl'],
'Anchor': ['a']
};
return function (HTMLElementConstructor) {
let match; let tagName = [];
if (typeof HTMLElementConstructor === 'function') {
match = /HTML(\w+)Element/.exec(HTMLElementConstructor.name);
if (match) {
tagName = elementNameLookupTable[match[1]] || [match[1].toLowerCase()];
}
}
return tagName;
};
}());
// Test:
console.log(tagNameOf(HTMLAnchorElement));
console.log(tagNameOf(HTMLMediaElement));
console.log(tagNameOf(HTMLHeadingElement));
It has to be noted that the function returns an array, since there are more than one possible corresponding tag names.

How can I insert an argument like forEach, reduce and the like does?

I'm trying to reinvent the wheel, sort of.. Just messing around trying to remake some jquery functions.. I've come this far
var ye = function (ele) {
if (ele[0] == "#")
{
return document.getElementById(ele.slice(1));
}
else if (ele[0] == ".")
{
// returns an array, use index
return document.getElementsByClassName(ele.slice(1));
}
else
{
// also returns an array
return document.getElementsByTagName(ele);
}
}
but how can I use this element as a parameter in a function in the 'ye' prototype. For example, if I wanted to make fontsize how could I get the dom element like here:
ye.prototype.fontSize = function (ele)
{
ele.style.fontSize = "30px";
}
Just to add a bit to make the title relevant.. forEach inserts three arguments into the callback function, just like I want ye to insert ele into the fontSize function.
Just messing around trying to remake some jquery functions...
...but how can I use this element as a parameter in a function in the 'ye' prototype..
Here is a very crude and simple way to start...
Create a function with a property called elems which is an array and will store the selected DOM elements.
Like this:
var oye = function() { this.elems = []; };
On its prototype, you can create your custom functions which you want to expose. e.g. the function fontSize (as in your question), iterate over the elems array property that we created earlier changing the font size of each DOM element stored in. this points to the instance which is calling this function which we will ensure to be of type oye later on. To enable chaining, we simply return itself via this.
Like this:
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
Now create the selector function called ye. This serves the purpose of selecting the DOM elements, storing them in the elems array property of a new instance of oye class, and return the instance. We call the slice of the array prototype to convert the nodeList to an array.
Like this:
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
Now start using it in your code. Just like jQuery, you can use ye to select and then call your custom functions.
Like this:
ye("#elem1").fontSize('30px');
Just like jQuery, you can also chain multiple custom functions as shown in the complete working example below:
ye("P").fontSize('24px').dim(0.4);
Next step: Remember this is just a very crude example. You can now proceed to club the step 1 and 2 into a single call using the init pattern returning the new object from the selector function itseld. Learn more about Javascript and best practices.
Here is a sample working demo:
var oye = function() { this.elems = []; };
oye.prototype.fontSize = function(size) {
this.elems.forEach(function(elem) {
elem.style.fontSize = size;
});
return this;
};
oye.prototype.dim = function(value) {
return this.elems.forEach(function(elem) {
elem.style.opacity = value;
});
return this;
};
var ye = function(elem) {
var newOye = new oye;
newOye.elems = [].slice.call(document.querySelectorAll(elem));
return newOye;
};
ye("#elem1").fontSize('30px');
ye(".elem2").fontSize('20px');
ye("P").fontSize('24px').dim(0.4);
<div>This is normal text.</div>
<div id="elem1">size changed via id.</div>
<div class="elem2">size changed via class.</div>
<div class="elem2">size changed via class.</div>
<p>size changed and dimmed via tag name</p>
<p>size changed and dimmed via tag name</p>
Regarding your question, I may think you're new to JavaScript, or not familiar with its basic concepts. I'm not sure reinventing the wheel is a good thing in such conditions.
Since you've cited jQuery, you can have a look at its source code to understand how it works under the hood:
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L17-L23
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core.js#L38-L81
https://github.com/jquery/jquery/blob/99e8ff1baa7ae341e94bb89c3e84570c7c3ad9ea/src/core/init.js#L19-L114
Having that said, I would have done something like this:
var ye = function ( ele ) {
return new ye.prototype.init(ele);
};
ye.prototype.init = function( ele ) {
this._elements = [].slice.call(document.querySelectorAll(ele));
return this;
};
ye.prototype.forEach = function( fn ) {
this._elements.forEach(fn);
return this;
};
ye.prototype.fontSize = function( fontSizeValue ) {
this.forEach(function (ele) {
ele.style.fontSize = fontSizeValue;
});
return this;
};
The associated usage is as follow:
var myCollection = ye('.someClassName');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
myCollection.fontSize('45px');
myCollection.forEach(function ( item, index ) {
console.log(item.style.fontSize);
});
Use ye function calling before setting style, something like:
ye.prototype.fontSize = function(ele) {
ye(ele).style.fontSize = '30px';
}
returned object should be richer, like that:
var baseObject = {
// Will be used for the element:
element: null,
width: function(){ return this.element.getwidth(); /* or anything similar*/ }
// ... Further methods
}
and then in your ye function:
var ye = function (ele) {
var yeElem = clone(baseObject); // See comment below!!
if (ele[0] == "#") { yeElem.element = document.getElementById(ele.slice(1)); }
else if (ele[0] == "."){ /*...*/ }
else { /*...*/ }
return yeElem;
}
This way the new element has built in methods.
As for the clone() method used, it doesn't exist but you have to use some clone method.
I recommend Loadsh's _.cloneDeep() (here).

Check if attribute is allowed per tag

I am attempting to create a program that performs many calculations and trying to reduce my dependency footprint. I will not require jQuery because I do not need to "manipulate" the DOM, but I would like the advantage of creating elements with ease.
Is there a way to determine if an attribute is legal for a given tag so that it will not be required to build a map for every case?
I could not find anything of use when searching SO and Google.
Here's a simple JSFiddle DEMO to mess around with.
var propMap = {
'text' : 'innerHTML'
};
var create = function(tagName, attributes) {
var el = document.createElement(tagName);
if (attributes !== undefined && typeof attributes === 'object') {
for (var prop in attributes) {
if (propMap[prop] !== undefined) {
el[propMap[prop]] = attributes[prop];
} else {
el.setAttribute(prop, attributes[prop]);
}
}
}
return el;
}
var el = create('span', { id : 'foo', class : 'bar', text : 'Hello World' });

Does d3 have api which similar with jQuery.closest(selector)?

DOM like this:
<g.module>
<g.control>
<rect>
I didn't find the closest API:
https://github.com/mbostock/d3/wiki/API-Reference
How can I get the nearest matched elements from it's parent? Just like this:
var module = d3.select(".control").closest(".module");
Browsers now have closest method on DOM node:
d3.select(rect.node().closest('svg'));
and similar code to #JayB with this method:
d3.selection.prototype.closest = function(selector) {
var arr = [];
this.each(function() {
arr.push(this.closest(selector));
});
return d3.selectAll(arr);
};
that allow to use just: rect.closest('svg');
the only problem is that it's not supported by IE
If your DOM is really that specific example, you can select parent nodes with D3 as noted in their docs here:
You can see the parent node of each group by inspecting the parentNode property of each group array, such as selection[0].parentNode.
So in your case, var module = d3.select(".control").parentNode; should work.
You could add "closest" to the d3 selection prototype like this:
d3.selection.prototype.closest = function (selector) {
var closestMatch = undefined;
var matchArr = [];
this.each(function () {
var elm = this;
while (typeof elm.parentNode.matches === "function" && !closestMatch) {
elm = elm.parentNode;
if (elm.matches(selector)) {
closestMatch = elm;
matchArr.push(closestMatch);
}
}
closestMatch = undefined;
});
return d3.selectAll(matchArr);
}
You can make the selection with jquery and pass that to d3.
var selection = $('.control').closest('.module');
var module = d3.select(selection);
This may or may not work depending on what you need it for, but might be a simple workaround.

how to turn this to into a tree?

I was doing a challenge of building a tree from all html elements. And I am 90% done, but I got stuck...
How do I change this string into a tree?:
mystring= "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
After splitting them by "-" we will have:
10 groups of -> (parameter1)/(parameter2)
The first parameter it is the object,
The second parameter is the 'in-what-it-will-be-contained'
I have no idea how to move every 'parameter1' inside 'parameter2'. (note: sometimes the parameter1 will be the parameter2 of a parameter1)
Visual example of what I mean with a parameter is inside another parameter: (this example uses exactly the string above)
Probably we should use arrays?, idk... I am totally lost :sadface:
I think this is a little more concise and straight forward. It uses an object as a dictionary to lookup the parent, rather than a function that has to recursively iterate the tree to find the parent. That recursive function is expensive. An object lookup is quick.
First, for convenience, I'd define an object type:
function TreeNode(name) {
this.Name = name;
this.Children = [];
}
Then I'd add a method to do the work. This parses your tree string:
TreeNode.ParseTree = function (treeString) {
var root = new TreeNode("");
var nodes = {};
var pairs = treeString.split("-");
pairs.forEach(function(pair) {
var parts = pair.split("/");
var parentName = parts[1];
var childName = parts[0];
var node;
if (parentName == "(none)") {
node = root;
root.Name = childName;
}
else {
node = new TreeNode(childName);
nodes[parentName].Children.push(node);
}
nodes[childName] = node;
});
return root;
};
That's it! Now, to get visual representations of your tree, you can add some prototype methods to TreeNode. First, override .toString():
TreeNode.prototype.toString = function(indent) {
indent = indent || "";
var strings = [indent + this.Name];
this.Children.forEach(function(child) {
strings.push(child.toString(indent + " "));
});
return strings.join("\n");
};
Then, add a .Render() method to display the tree within a web page:
TreeNode.prototype.Render = function(container) {
var nodeEl = container.appendChild(document.createElement("div"));
nodeEl.className = "treeNode";
var nameEl = nodeEl.appendChild(document.createElement("div"));
nameEl.className = "treeNodeName";
nameEl.appendChild(document.createTextNode(this.Name));
var childrenEl = nodeEl.appendChild(document.createElement("div"));
childrenEl.className = "treeNodeChildren";
this.Children.forEach(function(child) {
child.Render(childrenEl);
});
return nodeEl;
};
Here it is in action: http://jsfiddle.net/gilly3/wwFBx/
Edit: I didn't notice the jQuery tag in your post, here's a render method that's all jQuery, and produces simpler HTML which you seem to imply is what you want:
TreeNode.prototype.Render = function(container) {
var el = $("<div>").appendTo(container).text(this.Name);
$.each(this.Children, function() {
this.Render(el);
});
return el;
};
This JSFiddle uses jQuery, even replacing Array.forEach with $.each: http://jsfiddle.net/wwFBx/1/
As an alternative, you might consider just serializing your tree as JSON. Eg:
"{\"Name\":\"red1\",\"Children\":[{\"Name\":\"red2\",\"Children\":[{\"Name\":\"blue1\",\"Children\":[]},{\"Name\":\"blue2\",\"Children\":[]},{\"Name\":\"blue3\",\"Children\":[]}]},{\"Name\":\"red3\",\"Children\":[{\"Name\":\"red4\",\"Children\":[{\"Name\":\"red5\",\"Children\":[{\"Name\":\"red6\",\"Children\":[{\"Name\":\"blue4\",\"Children\":[]}]}]}]}]}]}"
or maybe:
"{\"red1\":{\"red2\":{\"blue1\":{},\"blue2\":{},\"blue3\":{}},\"red4\":{\"red5\":{\"red6\":{\"blue4\":{}}}}}}"
Parse the string via JSON.parse().
Disclaimer: I've referenced Array.forEach() and JSON.parse() which are built-in to modern browsers but are not supported by older browsers. To enable these functions in older browsers, see this documentation on Array.forEach() and this shim for JSON.parse().
Here's about how I would do it, using an array of "unplaced" elements and looping through it until they're all placed:
var str = "red1/(none)-red2/red1-blue1/red2-blue2/red2-blue3/red2-red3/red1-red4/red3-red5/red4-red6/red5-blue4/red6";
var unplaced = [];
var tree = null;
var elements = str.split(/[\/\-]/);
function findNodeByName(nodeName, context) {
if(context.name === nodeName) return context;
for(var i = 0; i < context.children.length; i++) {
var subSearch = findNodeByName(nodeName, context.children[i]);
if(subSearch) return subSearch;
}
return null;
}
var element, node, parent, thisElement, i;
for(i = 0; node = elements[i]; i += 2) {
node = elements[i];
parent = elements[i + 1];
thisElement = {name: node, children: []};
if(!tree && parent === '(none)') {
tree = thisElement;
} else if(tree) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(thisElement);
} else {
unplaced.push(thisElement);
}
}
}
var oldLength;
while(unplaced.length) {
oldLength = unplaced.length;
for(i = 0; element = unplaced[i]; i++) {
var parentNode = findNodeByName(parent, tree);
if(parentNode) {
parentNode.children.push(element);
unplaced.splice(i, 1);
i--;
}
}
if(oldLength === unplaced.length) {
throw new SyntaxError("The string is not a valid tree.");
}
}
// The result is contained in "tree".
You can see the result at: http://jsfiddle.net/minitech/tJSpN/
One with a function: http://jsfiddle.net/minitech/tJSpN/1/
And one with more error-checking: http://jsfiddle.net/minitech/tJSpN/2/
Actually, I found a simpler/shorter/neater way using the JQuery function AppendTo()
We just need to:
Split the parameters...
Create one div for each (parameter1)
Use a loop to move every (parameter1) inside (parameter2) using the
AWESOME AppendTo() function that JQuery offers
The best thing is that they are actually inside them, so you can easily put a Hide/Show effect to make a cool effect
You may try to create tree nodes of the form :
node = {
str:"red1",
subBranches : new Array()
}
Once you have that, you may add the sub-branches iterating through the array, adding such nodes for each found correct couple, and removing the couples already placed in rootNode.subBranches. Then you recursively do the same for every sub-branche.

Categories

Resources