Table of contents
  1. HTMLElement Interface
  2. Hide/Show
    1. CSS Style Hide
    2. Aria Hidden
    3. jQuery
  3. find / search / get by Attributes
    1. wild card / search ids where id is like
    2. check if exists
    3. jQuery
  4. Attribute Value
    1. Get
    2. Set
    3. Remove
    4. null check
    5. jQuery
  5. Node Types
    1. Node.ELEMENT_NODE (1)
    2. Node.ATTRIBUTE_NODE (2)
    3. Node.TEXT_NODE (3)
    4. Node.CDATA_SECTION_NODE(4)
    5. Node.PROCESSING_INSTRUCTION_NODE (7)
    6. Node.COMMENT_NODE (8)
    7. Node.DOCUMENT_NODE (9)
    8. Node.DOCUMENT_TYPE_NODE (10)
    9. Node.DOCUMENT_FRAGMENT_NODE (11)
    10. Check Type
      1. JavaScript check type on node element
    11. Deconstruct element to get node names and tag names / NodeName TagName
    12. Get Element Type
  6. create clone
  7. clone element to base64 png
  8. create table
  9. get children
  10. get un-rendered element dimensions
  11. Script Type
    1. Script Element defer / async / module, fetch, load, and execution timeline




HTMLElement Interface

Hide/Show

CSS Style Hide

element.style.display = "block"; // Show
element.style.display = "inline"; // Show
element.style.visibility = "visible"; // Show
element.style.display = "inline-block"; // Show

element.style.display = "none"; // Hide
element.style.visibility = "hidden"; // Hide
element.style.opacity = 0; // Hide
element.setAttribute("hidden", true); //hide

element.toggle(); // hide/show

Aria Hidden

let el = document.getElementById("hidden");
console.log(el.ariaHidden); // true
el.ariaHidden = "false";
console.log(el.ariaHidden); // false

jQuery

"element".show();

//also

$("element").toggle();

find / search / get by Attributes

document.querySelector("input[name=rate]:checked").value;

wild card / search ids where id is like

document.querySelector('[id^="poll-"]').id;

check if exists

document.body.contains(document.getElementById("test"));

jQuery

$("input[type='radio'][name='scheduleType']:not(:checked)").attr("disabled", true,);

find by text jQuery :contains(text)

Attribute Value

dataset returns DOMStringMap

Attribute must be camel cased Element.dataset.attributeNameCamelCased

Get

  • Attributes can be set and read by the camelCase name/key as an object property of the dataset: element.dataset.keyname
  • Attributes can also be set and read using bracket syntax: element.dataset['keyname']
  • The in operator can check if a given attribute exists: 'keyname' in element.dataset
document
    .getElementById("groupsWrapper-" + id)
    .getAttribute("data-saved-assessment-count");

or

imageContainer.dataset.images;

Set

When the attribute is set, its value is always converted to a string. For example: element.dataset.example = null is converted into data-example="null" To remove an attribute, you can use the delete operator: delete element.dataset.keyname

document
    .getElementById("groupsWrapper-" + id)
    .setAttribute("data-saved-assessment-count", "my text value");

or

// set a data attribute
el.dataset.dateOfBirth = "1960-10-03";

// Result on JS: 
el.dataset.dateOfBirth === '1960-10-03'
<!--Result on HTML: -->
<div data-date-of-birth="1960-10-03" data-id="1234567890" data-user="carinaanand" id="user">Carina Anand</div>

Remove

document
    .getElementById("groupsWrapper-" + id)
    .removeAttribute("data-saved-assessment-count");

or

delete el.dataset.dateOfBirth;

// Result on JS: 
el.dataset.dateOfBirth === undefined
<!--Result on HTML: -->
<div data-id="1234567890" data-user="carinaanand" id="user">Carina Anand</div>

null check

if (!("someDataAttr" in el.dataset)) {
    el.dataset.someDataAttr = "mydata";

    // Result on JS: 
    'someDataAttr' in el.dataset === true
}
<!--Result on HTML: -->
<div data-id="1234567890" data-some-data-attr="mydata" data-user="carinaanand" id="user">Carina Anand</div>

jQuery

$(selectElement).data("saved-assessment-count", currentTotalResultCount);

If using .data() to modify, you need to retrieve by .data() so new value will be reflected

Node Types

  • Node.ELEMENT_NODE (1)

    • An Element node

       <p>
      

      ~~ OR ~~

      <div>
      
  • Node.ATTRIBUTE_NODE (2)

    • An Attribute of an Element
  • Node.TEXT_NODE (3)

    • The actual Text inside an Element or Attr.
  • Node.CDATA_SECTION_NODE(4)

    • A CDATASection,

        <!CDATA[ … ](%E2%80%A6.md#)>
      
  • Node.PROCESSING_INSTRUCTION_NODE (7)

    • A ProcessingInstruction of an XML document,

        <?xml-stylesheet … ?>
      
  • Node.COMMENT_NODE (8)

    • A Comment node,

        <!-- … -->
      
  • Node.DOCUMENT_NODE (9)

    • A Document node
  • Node.DOCUMENT_TYPE_NODE (10)

    • A DocumentType node,

            <!DOCTYPE html>
      
  • Node.DOCUMENT_FRAGMENT_NODE (11)

    • A DocumentFragment node

         <div class="a">a</div>
      

Check Type

if (element.tagName === "OL") {
}
if (elementsObject && elementsObject?.body?.nodeName === "TABLE") {
}
const tagName = el.tagName || el.nodeName;
if (node?.nodeType) {
}
  • JavaScript check type on node element

      node = e;
      node.nodeType === 1;
      node.nodeName === "DIV";
      node.tagName === "DIV";
        
      node = e.getAttributeNode("class");
      node.nodeType === 2;
      node.nodeName === "class";
      node.tagName === undefined;
        
      node = e.childNodes[0];
      node.nodeType === 3;
      node.nodeName === "#text";
      node.tagName === undefined;
    
only use nodeType to get the node type.
nodeName breaks for nodeType === 1
only use tagName for nodeType === 1

Deconstruct element to get node names and tag names / NodeName TagName

function addBlankSelectOption(selectBox) {
    const {nodeName, tagName} = selectBox;
    if (nodeName === "SELECT" || tagName === "SELECT") {
        const blankOption = document.createElement("option");
        blankOption.setAttribute("selected", true);
        blankOption.setAttribute("disabled", true);
        blankOption.setAttribute("hidden", true);
        selectBox.prepend(blankOption);
    }
}

Get Element Type

Object.prototype.toString
      .call(obj)
      .replace(/^\[object (.+)\]$/, "$1")
      .toLowerCase();

create clone

function createClone(element, setCSS, width, height) {
    const newElement = element.cloneNode(true);

    const createStyles = new Promise((resolve) => {
        if (!setCSS) {
            resolve(true);
        }
        else {
            generateRulesAll(element)
                .then((css) => {
                    newElement.setAttribute("style", css);
                })
                .then(resolve);
        }
    });

    createStyles.then((css) => {
        if (width) {
            newElement.width = width;
            newElement.style.width = `${width}`;
        }

        if (height) {
            newElement.height = height;
            newElement.style.height = `${height}`;
        }
    });

    return newElement;
}

clone element to base64 png

using XMLSerializer

function svgToDataURL(svg) {
    return Promise.resolve()
                  .then(() => new XMLSerializer().serializeToString(svg))
                  .then(encodeURIComponent)
                  .then((html) => `data:image/svg+xml;charset=utf-8,${html}`);
}

function nodeToDataURL(node, width, height) {
    return new Promise((resolve) => {
        const xmlns = "http://www.w3.org/2000/svg";

        const foreignObject = document.createElementNS(xmlns, "foreignObject");
        foreignObject.setAttribute("width", "100%");
        foreignObject.setAttribute("height", "100%");
        foreignObject.setAttribute("x", "0");
        foreignObject.setAttribute("y", "0");
        foreignObject.setAttribute("externalResourcesRequired", "true");

        const svg = document.createElementNS(xmlns, "svg");
        svg.setAttribute("width", `${width}`);
        svg.setAttribute("height", `${height}`);
        svg.setAttribute("viewBox", `0 0 ${width} ${height}`);

        svg.appendChild(foreignObject);
        foreignObject.appendChild(node);
        resolve(svgToDataURL(svg));
    });
}

function createImage(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.crossOrigin = "anonymous";
        img.decoding = "sync";
        img.src = url;
    });
}

function buildCanvas(img) {
    return new Promise((resolveCanvas) => {
        const canvas = document.createElement("canvas");
        const context = canvas.getContext("2d");
        const canvasHeight = img.height + 50;
        const canvasWidth = img.width + 50;
        canvas.width = canvasWidth;
        canvas.height = canvasHeight;
        canvas.style.width = `${canvasWidth}`;
        canvas.style.height = `${canvasHeight}`;
        context.drawImage(img, 0, 0, canvas.width, canvas.height);

        resolveCanvas(defaultSlideObject(canvas.toDataURL(), canvas.width, canvas.height),);
    });
}

//this is similar to how html2canvas and html-to-image libraries work
// take element -> get generated css -> create ans svg(needed to paint canvas element) ->
// use svg to create xml then uri then uri data string -> use data uri to create image element by adding uri to src attribute ->
// create a canvas element inserting/drawing image in -> get dataURL to use
export function createElementToPNGBase64(arrayOfElementObjects, dimensionsObj, addIndividualElementCSS,) {
    if (arrayOfElementObjects) {
        return new Promise((resolve) => {
            elementsToNewDocument(arrayOfElementObjects, dimensionsObj, addIndividualElementCSS,)
                .then(({clonedDocument, width, height, newElementArray}) => nodeToDataURL(clonedDocument.documentElement, width, height),)
                .then(createImage)
                .then(buildCanvas)
                .then(resolve);
        });
    }
}

export function cloneNodeToDocument(node, elementToAppend, idToSet, width, height, addIndividualElementCSS,) {
    if (node) {
        const w = width
                  ? width
                  : node.scrollWidth;
        const h = height
                  ? height
                  : node.scrollHeight;
        const newNode = createClone(node, !!addIndividualElementCSS, w, h);
        newNode.id = idToSet;
        elementToAppend.appendChild(newNode);
        return newNode;
    }
}

//array of element objects example: [{key: element}]
// dimension object key should match element if custom dimension needed
// example: {key:{height: number, width: number}}
export function elementsToNewDocument(arrayOfElementObjects, dimensionsObj, addIndividualElementCSS,) {
    return new Promise((resolveNewDocument) => {
        const usableArray = Array.isArray(arrayOfElementObjects)
                            ? arrayOfElementObjects
                            : [arrayOfElementObjects];

        const clonedDocument = document.cloneNode(true);
        // documentElement.querySelectorAll("noscript").forEach(el => el.remove())
        const body = clonedDocument.body;
        body.innerHTML = "";

        let totalWidth = 0;
        let totalHeight = 0;

        const clonedElementArray = usableArray.map((elementObj) => {
            const key = Object.keys(elementObj)[0];
            const element = elementObj[key];
            const width = Object.hasOwn(dimensionsObj, key)
                          ? dimensionsObj[key]?.width
                          : element.scrollWidth;
            const height = Object.hasOwn(dimensionsObj, key)
                           ? dimensionsObj[key]?.height
                           : element.scrollHeight;

            totalWidth = totalWidth > width
                         ? totalWidth
                         : width;
            totalHeight += height;
            cloneNodeToDocument(element, body, key, width, height, addIndividualElementCSS,);
        });

        resolveNewDocument({
            clonedDocument: clonedDocument, width: totalWidth, height: totalHeight, newElementArray: clonedElementArray,
        });
    });
}

create table

export const createRow = async (row, index, newBody) => {
    const newRow = newBody.insertRow(index);
    newRow.innerHTML = row.innerHTML;
    return newRow;
};

export const createTable = async (tableRows, rowIndex, elementsObject, divToAppend,) => {
    if (elementsObject && elementsObject?.body?.nodeName === "TABLE") {
        const table = elementsObject.body;
        const tableId = `clone_table_${rowIndex + 1}`;
        const {
                  nodeClone: newTable, widthToSet, heightToSet,
              } = buildNewNodeClone(table, tableId, null, null, true, true);
        newTable.tBodies[0].remove();
        const tBody = newTable.createTBody();
        const tFoot = newTable.createTFoot();
        const tHead = newTable.createTHead();
        tHead.innerHTML = table.tHead.innerHTML;
        tFoot.innerHTML = table.tFoot.innerHTML;

        const rowPromises = tableRows.map((row, index) => new Promise((resolve) => resolve(createRow(row, index, tBody))),);
        await Promise.all(rowPromises).then((resolved) => {
            console.log(`rows for table ${tableId} added`);
        });

        const tableWrapper = document.createElement("div");
        tableWrapper.id = `clone_table_wrapper_${rowIndex + 1}`;
        if (elementsObject?.header) {
            const {
                      nodeClone: newHeader, widthToSet, heightToSet,
                  } = buildNewNodeClone(elementsObject?.header, null, null, null, true, true,);
            tableWrapper.appendChild(newHeader);
        }

        tableWrapper.appendChild(newTable);
        if (divToAppend) divToAppend.appendChild(tableWrapper);

        return tableWrapper;
    }
};

get children

export const getChildren = (element) => {
    let childArray = [];
    childArray.push(element);

    const children = element?.children;
    if (children) {
        for (let child of children) {
            childArray.push.apply(childArray, getChildren(child));
        }
    }

    return childArray;
};

get un-rendered element dimensions

function getWidthAndHeight(node) {
    const width = node.scrollWidth || node.offsetWidth || measure(node, "scrollWidth");
    const height = node.scrollHeight || node.offsetHeight || measure(node, "scrollHeight");
    return {width, height};
}

function measure(element, returnValue, parentNode) {
    parentNode = parentNode || window.document.documentElement.lastElementChild;
    const display = element.style.display;
    const zIndex = element.style.zIndex;
    const visibility = element.style.visibility;

    element.style.display = "block";
    element.style.visibility = "hidden";
    element.style.zIndex = -1;
    parentNode.appendChild(element);
    const {
              clientWidth, clientHeight, offsetWidth, offsetHeight, scrollWidth, scrollHeight,
          } = element;
    parentNode.removeChild(element);
    element.style.display = display;
    element.style.visibility = visibility;
    element.style.zIndex = zIndex;

    const returnObj = {
        clientWidth:  clientWidth,
        clientHeight: clientHeight,
        offsetWidth:  offsetWidth,
        offsetHeight: offsetHeight,
        scrollWidth:  scrollWidth,
        scrollHeight: scrollHeight,
    };

    if (returnObj.hasOwnProperty(returnValue)) {
        return returnObj[returnValue];
    }

    return {
        clientWidth, clientHeight, offsetWidth, offsetHeight, scrollWidth, scrollHeight,
    };
}

Script Type

Classic <script>s block the HTML parser by default.
You can work around it by adding the defer attribute, which ensures that the script download happens in parallel with HTML
parsing.

Script Element defer / async / module, fetch, load, and execution timeline