I'm looking for the shortest syntax which could provide me the same result as this dojo line:
var divblock5 = dojo.create("div", {className: "barlittle", id: "block5"});
but I want to use plain JavaScript instead of dojo framework. I have a lot of dynamic element creation and I want to make my code short as possible.
var create = function(element, properties) {
var elmt = document.createElement(element);
for (var prop in properties) {
elmt[prop] = properties[prop];
}
return elmt;
}
create("div", {className: "barlittle", id: "block5"});
Or, my personal favorite that simply takes HTML and converts it to a DOM node :
var elmtify(html) {
var wrapper = document.createElement('div');
wrapper.innerHTML = html;
return wrapper.firstChild;
}
elmtify('<div class="barlittle" id="block5"></div>');
You should check put-selector: https://github.com/kriszyp/put-selector.
Since this seems to be a still open question I'm adding my method, which is almost the same as Tom said but my approach takes into account style information:
function createElement(tag, attrs) {
if(!tag) throw new SyntaxError("'tag' not defined"); // In case you forget
var ele = document.createElement(tag), attrName, styleName;
if(attrs) for(attrName in attrs) {
if(attrName === "style")
for(styleName in attrs.style) {ele.style[styleName] = attrs.style[styleName];}
else
ele[attrName] = attrs[attrName];
}
return ele;
}
So if you normally write, without any library:
var divBlock5 = document.createElement("div");
divBlock5.className = "barlittle";
divBlock5.id = "block5";
With the snippet Tom provided, you would just write:
var divBlock5 = createElement("div", {className:"barlittle", id:"block5"});
But say you want to add an independent style to your element. Then, with my addition, you write:
var divBlock5 = createElement("div", {className:"barlittle", id:"block5", style:{color:"#08A", fontWeight:"bold"}});
Hope this helps. Cheers!
function createElement(tag, attrs, html) {
if (!tag) throw new SyntaxError("'tag' not defined"); // In case you forget
var ele = document.createElement(tag),
attrName, styleName;
if (attrs)
for (attrName in attrs) {
if (attrName === "style")
for (styleName in attrs.style) { ele.style[styleName] = attrs.style[styleName]; }
else if (attrs[attrName])
ele.setAttribute(attrName, attrs[attrName]);
}
if (html)
ele.innerHTML = html;
return ele;
}
This solution builds on what Tom and Xch3l proposed, but it adds support for passing custom attributes to be applied to the DOM element. Which attributes exist as properties for any given DOM element depends on the DOM element's type, so using the element's setAttribute method is a more robust solution.
Note that you will need to pass a "class" attribute instead of "className," and that this solution also adds support for an optional HTML string that can be passed as the third parameter.
Here is an example of how to use the function:
var divBlock5 = createElement("div", {class:"barlittle", "data-my-custom-attribute":"lorem ipsum", id:"block5", style:{color:"#08A", fontWeight:"bold"}},`<h5>An Optional String of HTML to be Inserted Inside the Newly Created Element</h5><p>Preserve Line Spacing by Using Backticks Instead of Quotes</p>`);
createElement = function(type, className, id) {
var element = document.createElement(type);
element.className = className;
element.id = id;
return element;
}
Related
I am reading a book about JavaScript specifically the chapter that talks about chaining methods. I know how chaining works. This is what i've done so far(An example of DOM chaining).
var app = {};
app.dom =
{
createElement : function(type)
{
this.element = document.createElement(type);
this.html = function(html)
{
this.element.innerHTML = html;
return this;
};
this.css = function(prop,value)
{
this.element.style[prop] = value;
return this;
}
this.getElement = function()
{
return this.element;
}
}
}
new app.dom.createElement('p').html('hello word').css('border','1px solid');
This code works fine. it creates a p element , adds html code inside and adds css properties. But my question is how can i append this created node to body without doing this (calling the getElement which returns the node element)
document.body.appendChild(new app.dom.element('p').html('hello world').css('border','1px solid').getELement());
Instead, i dont want to use the getElement method of the constructor function, just every time i invoke the html method or css method, the node element(this.element) can be appended to body directly, like this:
document.body.appendChild(new app.dom.element('p').html('hello world').css('border','1px solid'));
Is this possible?
As your code is bad already, You can write something similar to this (It's very bad idea, So please DO NOT USE THIS CODE IN PRODUCTION):
var app = {};
app.dom = {
createElement: function createElement(type) {
element = document.createElement(type);
element.html = function(html) {
this.innerHTML = html;
return this;
}.bind(element);
element.css = function(prop, value) {
this.style[prop] = value;
return this;
}.bind(element);
return element;
}
};
document.body.appendChild(app.dom.createElement('p').html('hello world').css('border','1px solid'));
In my code like ,
function ElementBase(name) {
this.tagName = typeof name != "" ? name : 'div';
this.createElem();
}
ElementBase.prototype = {
createElem: function() {
this.elem = document.createElement(this.tagName);
},
getIndex: function() {
var nodes = this.elem.parentNode.childNodes,
node;
var i = count = 0;
while ((node = nodes.item(i++)) && node != this.elem)
if (node.nodeType == 1) count++;
return (count);
}
};
I try to create the DOM element tag is "div".
function Div() {
this.tagName = 'div'
ElementBase.call(this, this.tagName);
}
Div.prototype = Object.create(ElementBase.prototype);
My Question is,
1) How to access the getIndex function from the html document after inserting the created objects?
example:
var div = new Div();
div.id = "d1"
document.body.appendChild(div.elem);
// After div.getIndex() working
Then some situation i need the index value of that div (id="d1") element from document.
var d= document.getElementById("d1");
d.getIndex() //not working
What mistakes i did it in above code?
thanks advance..
I think when you do document.body.appendChild(div.elem) you just do document.body.appendChild(document.createElement('div')) nothing more.
And when you do var d= document.getElementById("d1"); d is just an object return from the DOM that has nothing to do with your var div
what you can do is:
Div.prototype.getIndex.call(d);
But that doesn't actually extend your object. Actually extending a DOM object is a bad practice (check this http://perfectionkills.com/whats-wrong-with-extending-the-dom/).
Look closely at your code.
div is an instance of Div and it has a property .elem that holds the actual DOM element.
So when you do div.id = "d1", you are not setting the id of the DOM element.
var div = new Div();
div.id = 'd1'; // <div></div>
div.elem.id = 'd1'; // <div id="d1"></div>
But there's one more problem: when you do d= document.getElementById("d1"), what you get is a DOM element, not an instance of Div().
Since .getIndex() is defined on .prototype of Div(), plain old DOM elements don't have access to it.
How you solve this situation depends on what exactly you need to accomplish with your code.
Edit 1: In response to OP's comment:
document.getElementById() returns an instance of HTMLDivElement, which is fundamentally different from an instance of Div.
One solution is to use a setter method:
function Div() {
// ...
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div();
div.setId('d1'); // same as doing div.elem.id = 'd1';
another solution is to use id in the constructor function itself:
function Div(id) {
// ...
this.elem.id = id; // or you can use "this.setId(id)"
/*
if "id" is provided,
it will take that value,
else it is set to "undefined",
which is the same as not being set
*/
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div('d1'); // same as doing div.elem.id = 'd1';
div.setId('d2'); // same as doing div.elem.id = 'd2';
I'm trying to create a function using vanilla JS that will:
Create a new DOM element
Assign it a Class Name
Place it in the DOM either appending to an existing div or inserting it specifically into the DOM if required using "insertBefore()"
I have come up with the somewhat inelegant solution below:
function createDomElem(elem, className, parent, refElement, type) {
var a = document.createElement(elem);
if (type == "append") {
document.querySelector(parent).appendChild(a);
} else if (type == "insert") {
document.querySelector(parent).parentNode.insertBefore(a, refElement)
}
a.className = className;
};
My problems with this solution are
Too many arguments to be passed
If not passing "insert" then you don't require refElement and to avoid "type" being mistaken for "refElement" you'd have to pass "refElement" as "null" and then define type as "append"
So my question is where can I streamline this function to become more useful within my program?
I'm also dreaming of the ability to be able to push child divs into the newly created div right within this function, defining how many child divs you would want and then using a for loop to append or insert these. Would this be better placed in a new function though?
I would split the code into two parts, as they have to separate concerns. I use something similar to the following for creating DOM elements:
var DomFactory = (function (document) {
var api = {
element: function (name, attributes) {
var el = document.createElement(name);
if (attributes) {
for (var key in attributes) {
if (attributes.hasOwnProperty(key)) {
el.setAttribute(key, attributes[key]);
}
}
}
return el;
},
div: function (attributes) {
return api.element('div', attributes);
}
};
return api;
}(window.document));
Usage:
var div = DomFactory.div({ 'class': 'hero' });
var table = DomFactory.element('table', { 'class': 'table table-bordered' });
Then for positioning, you could have a generalised position function:
function attach(source, target, position) {
switch (position) {
case 'before': {
target.parentNode.insertBefore(source, target);
break;
}
case 'after': {
if (target.nextSibling) {
target.parentNode.insertBefore(source, target.nextSibling);
} else {
target.parentNode.appendChild(source);
}
}
}
}
Usage:
attach(table, div, 'before');
I've got these functions to create elements and change their attributes. Could you give me an advice on how to modify them?
function create(elem) {
return document.createElementNS ? document.createElementNS("http://www.w3.org/1999/ xhtml", elem) : document.createElement(elem);
}
function attr(elem, name, value) {
if (!name || name.constructor != String) return "";
name = {"for": "htmlFor", "class": "className"}[name] || name;
if (typeof value != "undefined") {
elem[name] = value;
if (elem.setAttribute) elem.setAttribute(name, value);
}
return elem[name] || elem.getAttribute(name) || "";
}
I want to get something like this create('div', {'id': 'test', 'class': 'smth'});
function create(elem, attr) {
if (!attr) return document.createElementNS ? document.createElementNS("http://www.w3.org/1999/xhtml", elem) : document.createElement(elem);
if (attr) {
var el = document.createElementNS ? document.createElementNS("http://www.w3.org/1999/xhtml", elem) : document.createElement(elem);
for (var i = 0; i < attr.length; i++) {
attr(el, name[i], value[i]);
}
return el;
}
}
Please help =]
You did pretty good but I have a solution for you that you should try that worked for me and it is quick and easier. It for the creating a element and sets attributes function.
as you mentioned:
I want to get something like this create('div', {'id': 'test', 'class': 'smth'});
here is the solution:
function create(ele, attrs) {
//create the element with a specified string:
var element = document.createElement(ele);
//create a for...in loop set attributes:
for (let val in attrs) {
//for support in the setAttrubute() method:
if (element.setAttribute) {
if (element[val] in element) {
element.setAttribute(val, attrs[val]);
} else {
element[val] = attrs[val];
}
} else {
element[val] = attrs[val];
}
}
//return the element with the set attributes:
return element;
}
This also works with custom attributes and it property's like innerHTML too.
If you also want to be sure that I know this works I have tested it and logged it on the console and seeing it on the HTML page. I tested this on Firefox.
Here's a Demo
You can't iterate through an object like that:
for (var k in attrs) {
if (attr.hasOwnProperty(k))
attr(el, k, attrs[k]);
}
Note that I changed your "attr" variable to "attrs" so that it doesn't hide the "attr" function you've created. Also, up in your "attr" function, change the "undefined" test:
if (typeof value !== undefined)
to be a little safer. Comparisons with "==" and "!=" attempt a type conversion, which is unnecessary if you're just checking undefined.
I would recommend a javascript framework like jQuery. They already have this functionality implemented.
$("<div/>", {
"class": "test",
text: "Click me!",
click: function(){
$(this).toggleClass("test");
}
}).appendTo("body");
A word of advice: I personally prefer the jquery way because you can add the css and events to the element directly, and refer to objects by a var name instead of the id, but... There are issues when using this method to create input elements, ie7 & ie8 don't allow you to set the type property so beware when creating a button, textbox, etc for example, jquery will throw a "type property can't be changed" error.
If the code is to be used in a browser before ie9, best use: document.createElement instead to increase compatibility.
export function Element(name, object = {}) {
const element = document.createElement(name);
for (const key in object) {
element[key] = object[key];
}
return element;
}
export function Anchor(object) {
return Element('a', object);
}
Use it like:
const anchor = Anchor({href: 'test'});
I am building a drag'n'drop gui builder in Javascript. So far so good.
As I add items to the GUI and configure them; I have two mechanisms for addressing them:
the 'class' - which I use for doing things to all instances of an item (eg CSS, generic functionality and so on and so forth) and which I can bind javascript libraries to... and I can make full use of polymorphic class names (ie class="name1 name2 name3 name4" with different things bound to each class name...)
the 'id' - which refers to this particular instance of a text box or a paragraph and which I can bind javascript libraries to
My problem is this: the 'id' must be unique across all html items on the page (by definition) so how do I ensure this? I need to get all the id's of all the items and then maintain some sort of state table.
Starting from a blank bit of html this is pretty reasonable - but I need to start from a partly created bit of html with a mixture of existing 'id's - some of which will be in my unique scheme and some of which wont be...
The way to do this best ought to be a solved problem.
Suggestions, tips, examples?
The best way to do this will depend entirely upon the structure and organization of your javascript. Assuming that you are using objects to represent each of your GUI elements you could use a static counter to increment your ids:
// Your element constructor
function GuiElement() {
this.id = GuiElement.getID();
}
GuiElement.counter = 0;
GuiElement.getID = function() { return 'element_' + GuiElement.counter++; };
Of course you probably have more than one type of element, so you could either set each of them up so that they have their own counter (e.g. form_1, form_2, label_1, label_2) or so that they all share a counter (e.g. element_1, element_2, element_3), but either way you will probably want them to inherit from some base object:
// Your base element constructor
function GuiElement(tagName, className) {
this.tagName = tagName;
this.className = className;
}
GuiElement.counter = 0;
GuiElement.getID = function() { return 'element_' + GuiElement.counter++; };
GuiElement.prototype.init = function() {
this.node = document.createElement(this.tagName);
this.node.id = this.id = GuiElement.getID();
this.node.className = this.className;
}
// An element constructor
function Form() {
this.init();
}
Form.prototype = new GuiElement('form', 'form gui-element');
// Another element constructor
function Paragraph() {
this.init();
}
Paragraph.prototype = new GuiElement('p', 'paragraph gui-element');
You could also go this route if you would rather keep some variables "private":
// Your element constructor constructor
var GuiElement = (function() {
var counter = 0;
function getID() {
return 'element_' + counter++;
}
return function GuiElement(tagName, className) {
return function() {
this.node = document.createElement(tagName);
this.node.id = this.id = getID();
this.node.className = className + ' gui-element';
this.className = className;
};
}
})();
// Create your element constructors
var Form = GuiElement('form', 'form'),
Paragraph = GuiElement('p', 'paragraph');
// Instantiate elements
var f1 = new Form(),
f2 = new Form(),
p1 = new Paragraph();
Update: If you need to verify that an id is not already in use then you could add the check you and of the getID methods:
var counter = 0;
function getID() {
var id = 'element_' + counter++;
while(document.getElementById(id)) id = 'element_' + counter++;
return id;
}
function uniqueId() {
return 'id_' + new Date().getTime();
}
If you happen to be using the Prototype library (or want to check it out), you can use the Element.identify() method.
Otherwise, Darin's response is a good idea as well.
function generateId() {
var chars = "0123456789abcdefghiklmnopqrstuvwxyz",
string_length = 8,
id = '';
for (var i = 0; i < string_length; i++) {
var rnum = Math.floor(Math.random() * chars.length);
id += chars.substring(rnum, rnum + 1);
}
return id;
}
Close enough to unique is good enough. Don't use the Date() solution unless you're only generating a single ID at any given time...