Skip to content

Instantly share code, notes, and snippets.

@trumad
Last active October 5, 2022 08:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trumad/c4d99f856c0ca3dd969657f5c847ab86 to your computer and use it in GitHub Desktop.
Save trumad/c4d99f856c0ca3dd969657f5c847ab86 to your computer and use it in GitHub Desktop.
A javascript helper function to add elements to the DOM
// Helper function to create new DOM elements, by thanael - thannymack.com
function addEl() {
function isElement(element) { // Check if something is a DOM element
// This function from https://stackoverflow.com/a/36894871
return element instanceof Element || element instanceof HTMLDocument;
}
// Defaults, if the user specifies nothing:
let el = document.body; // add our new element to the document.body by default
let tag = "div"; // add a DIV element by default
let attr = {}; // no attributes by default
for (let i = 0, j = arguments.length; i < j; i++) { // for each argument the user supplied
// Idea from https://stackoverflow.com/a/51019068
if (isElement(arguments[i])) { // if it's a DOM element
el = arguments[i]; // assign it to the el variable
}
else if (typeof arguments[i] === "object") { // if it's a normal object
attr = arguments[i]; // assign it to the attributes variable
}
else if (typeof arguments[i] === "string") { // or if it's a string
tag = arguments[i]; // assign it to the tag name variable
}
else {
return "You must specify a tag name (string) or a DOM element, or an object full of attribute names & values";
}
}
const potentialHeadTags = ["style", "title", "link", "meta", "script", "base"]; // hardcoded list of potential tags that belong in <head>
if (potentialHeadTags.includes(tag.toLowerCase()) && el === document.body) {
// If the user has specified a tag usually put in the head,
// and haven't supplied any other element:
el = document.head; // we'll be appending our new element in the document.head
}
let insertBefore = false; // the user wants to insert the element before the element they specified in "el". Off by default.
let append = true;
let newEl = document.createElement(tag); // create the new element
let attrKeys = Object.keys(attr); // generate an array of all attribute names the user supplied
for (let i = 0, j = attrKeys.length; i < j; i++) { // for each attribute
if (attrKeys[i] === "insertBefore") { // Checks for the presence of the "insertBefore" directive
insertBefore = Boolean(attr[attrKeys[i]]); // convert to a boolean and assign
}
else if (attrKeys[i] === "append") { // Checks for the presence of the "append" directive
append = Boolean(attr[attrKeys[i]]); // convert to a boolean and assign
}
else if (attrKeys[i].toLowerCase().startsWith("on") && typeof (attr[attrKeys[i]]) === "function") { // if the user is trying to add an event handler
let handler = attrKeys[i].match(/on(.*)/i)[1]; // Regex out the actual event handler
newEl.addEventListener(handler, attr[attrKeys[i]]);
}
else if (attrKeys[i] in el && !attrKeys[i].toLowerCase().startsWith("on")) {
// if the user wants to specify a dot notation property (textContent etc)
// "in" syntax from https://dmitripavlutin.com/check-if-object-has-property-javascript/
// added the check for !startsWith("on") to handle edge cases where the user wants to supply a string containing JS
// otherwise javascript will do el.onmouseover = "alert()" which achieves nothing.
newEl[attrKeys[i]] = attr[attrKeys[i]]; // set the related element property to be whatever they asked for
}
else { // otherwise, they've just specified a regular attribute
newEl.setAttribute(attrKeys[i], attr[attrKeys[i]]); // set it and forget
}
}
if (!append){return newEl} // The user wants to append the element later. Return it to them as-is
if (insertBefore) { // If the user assigned this as true, this is where we insert the element before the element they specified
el.parentNode.insertBefore(newEl, el);
}
else { // Otherwise, we just append the element to the page
el.appendChild(newEl)
}
return newEl;
}
// Usage:
// The function expects any of 3 types of arguments:
// * a tag name for your new element (string) - default: "div"
// * a DOM element you want to add your new element to - default: document.body
// * an object containing a list of attributes - default: {}. Example: {textContent:"Hello World", class: "dark", onclick: functionName}
// Don't try to supply two strings, or two DOM elements etc.
// The function will return the newly created element.
// Examples:
// Elements are added to the document.body by default:
// addEl({textContent:"A new div on the document body"});
// You can specify which element to add to:
// addEl("li", document.querySelector("ul"), {innerText:"A new list item",});
// You can specify an element and insert your new element in front of it by setting insertBefore to true (or anything that coerces to true!)
// addEl("li", document.querySelector("li"), {textContent:"A bumped first list item", insertBefore:true});
// You can create an element but not add it to the page, using the "append" attribute boolean:
// addEl("div",{textContent:"loading...", class:"main", append:false});
// You can set values
// addEl("input",{value:"Joe Bloggs"});
// Set event handler function names (Where handleEvent is the name of a function you've written)
// addEl("button", {textContent:"mouseoverme!", class:"btn", onmouseover:handleEvent});
// Set inline styles on the fly:
// addEl({textContent:"Warning!", style:"color: red;"});
// You can specify innerHTML if you realllly want to.
// addEl("span", {innerHTML:"<button>another button</button>"});
// You can also specify your own event handler javascript inside a string
// addEl("button",{textContent:"clickme!", class:"btn",onclick:"alert()"});
// It detects when a tag should be added to the head rather than the body:
// addEl("style", {textContent:"div {color: red;}"});
// addEl("script", {textContent:"alert()"});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment