Last active
October 5, 2022 08:56
-
-
Save trumad/c4d99f856c0ca3dd969657f5c847ab86 to your computer and use it in GitHub Desktop.
A javascript helper function to add elements to the DOM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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