Adding Buttons

Quick Method

Add attribute onClick='functionName()' to the desired element

// In HTML
<button class="menu-btn" id="menuBtn" onClick="myFunction()">&#9776;</button>

<script>
function myFunction() {
    console.log("Button clicked!");
}
</script>

Modular Method

Use addEventListener() to bind the element to the function and the 'click' event

// In HTML
<head>
<script type="module">
    import { bindButton } from '/load-menu.js';

    document.addEventListener('DOMContentLoaded', () => {// wrap the callback function 
                                                        (bindButton) in a function so it doesn't run immediately when script loads
        // Bind button after DOM is loaded
        <mark>bindButton("menuBtn");</mark>
    });
</script>
</head>

<body>
    <button class="menu-btn" <mark>id="menuBtn">&amp;#9776;</button>
</body>

// In load-menu.js
// Function to bind button click event
export function bindButton(buttonId) {
    document.getElementById(buttonId).addEventListener('click', myFunction);>
}
// Declare the function to be called when button clicked
function myFunction() {
    console.log("Button clicked!");
}

Finding Elements

The main methods for getting an element in the DOM are getElementById,querySelector, querySelectorAll

  • getElementById can only be used by Document objects (i.e.document)
  • querySelector and querySelectorAll can be used by bothHTMLElement and Document objects
// In Javascript
// Returns the element with id="menuBtn"
const menuBtn = document.getElementById("menuBtn");   

// Returns the first button element with class="menu-btn"
const menuBtn = document.querySelector("button .menu-btn");  

 // Returns a NodeList of all button elements with class="menu-btn"
const menuBtns = document.querySelectorAll("button .menu-btn");

// Looking for a #menuBtn starting from a specific element, topNav
const topNav = document.getElementById("topNav"); 
const menuBtn = topNav.querySelector("#menuBtn");

Element Object

The Element object has generic DOM manipulation methods and properties.

Commonly Used Methods and Properties

  • querySelector(selector) : Returns the first matching descendant element.
  • querySelectorAll(selector) : Returns a NodeList of all matching descendants.
  • getAttribute(name) / setAttribute(name, value) /removeAttribute(name) : Attribute handling.
    • document.documentElement.setAttribute('data-theme', 'dark') : add the custom attribute data-theme='dark' to the root-level element (i.e.<html$gt;element)
    • Very useful to change the 'state' of the overall site
  • append(...nodesOrStrings) / prepend(...nodesOrStrings) : Insert content inside the element.
    • document.body.prepend(node) : add a node as the first child of the<body>
  • before(...nodesOrStrings) / after(...nodesOrStrings) /replaceWith(...nodesOrStrings) : Insert or replace relative to the element itself.
    • oldTopNav.replaceWith(newTopNav) : replace the old top nav element with the newly created one
  • classList : Property with methods like add(), remove(),toggle(), contains().
    • leftNav.classList.toggle('open') : add/ remove the class 'open' from the element leftNav
      • Very useful to switch the 'state' of an element

Other Methods and Properties

  • hasAttribute(name) : Check if attribute exists.
  • matches(selector) : Checks if the element would be selected by a given CSS selector.
  • closest(selector) : Finds the nearest ancestor (including itself) matching a selector.
  • remove() : Removes the element from the DOM.

Reference: developer.mozilla.org/en-US/docs/Web/API/Element


HTMLElement Object

  • style : Access inline CSS styling via JS (e.g.,element.style.color = 'red').
  • innerText / innerHTML / outerHTML : Get or set the element's content.
  • focus() / blur() : Manage element focus.
  • click() : Programmatically triggers a click event.
  • hidden : Boolean property to toggle whether the element is hidden.
  • tabIndex : Controls keyboard tab navigation order.
  • contentEditable : Makes element text editable in the browser.
  • dataset : Access data-* attributes (e.g., div.dataset.id).

Reference: developer.mozilla.org/en-US/docs/Web/API/HTMLElement


Creating Elements

Creating Elements

Use document.createElement('template') to create a template element where theinnerHTML can be replaced with new element's code

// In Javascript

// raw HTML code for the new button
const btnHTML = `
    <button class="menu-btn" id="menuBtn">&amp;#9776;</button>
`;   

// Create a new button element
function createButton() {
    const btnTemp = document.createElement('template');
    btnTemp.innerHTML = btnHTML.trim();        // Set the HTML code
    const newBtn = btnTemp.content.firstChild;  // Get the element node 
    return newBtn;                    
}

Notes

  • Use backticks ` in order to create a template literal string - string in Javascript that spans multiple lines and uses embedded expression ( i.e. ${variableName} )
  • Use content.children to get all the nodes if multiple elements were created

Editing DOM

Adding Elements

Use methods like prepend(), append(), before(), andafter() to add element(s) into the DOM

// In Javascript
const topNav = createTopNav(); // Create the top nav element
document.body.prepend(topNav); // Add the top nav as the first child of the body

Replacing Elements

Use replaceWith() method to replace element.

// In Javascript
const topNav = createTopNav(); // Create the top nav element
const oldTopNav = document.getElementById('topNav'); // Get the old top nav element
oldTopNav.replaceWith(topNav); // Replace the old top nav with the new one

Getting and Setting Element Styles

  • Use window.getComputedStyle(element).getPropertyValue('css-property') to get the current property value as a String
  • To SET the inline property styles, use element.style.cssProperty
  • Trying to read the CSS property value with element.style.cssProperty can give an empty string because it reads the inline value
// Reading the CSS property rule
const grid = document.getElementById('grid');
const rowTrackHeightString = window.getComputedStyle(grid).getPropertyValue('grid-auto-rows');
rowTrackHeight = parseInt(rowTrackHeightString);

// Setting the inline style
grid.style.gridAutoRows = someValue

Getting Element Sizes

Generic HTML Element Properties

  • style.width / style.height : Inline style value, as a String; if not set, returns empty string
  • clientWidth / clientHeight : inner size of the element as an integer (includes padding, but excludes borders)
  • offsetWidth / offsetHeight : Rendered size as an integer (includes padding + border)
  • getBoundingClientRect().width / height : Precise rendered size as a float (reflects transforms and includes padding + border)
  • getComputedStyle(element).width / height : Resolved CSS width/height value (e.g., '200px' or 'auto')

Image-, Video-, & Canvas-only Sizes

  • img.naturalWidth / img.naturalHeight : Actual image size in pixels (only for <img>)
  • video.videoWidth / video.videoHeight : Actual video resolution (only for <video>)
  • canvas.width / canvas.height : Internal drawing buffer size (only for <canvas>)
  • img.width/height & video.width/height : Reflects the HTML attributes; sets rendered size (not intrinsic)

Local/Session Storage

localStorage is a persistent data storage for the web unlike sessionStorageunless the user is incognito mode, clears cookies, or storage is full

Methods

  • localStorage.setItem(key, value) : Store a key-value pair.
  • localStorage.getItem(key) : Retrieve the value for a given key.
  • localStorage.removeItem(key) : Remove a key-value pair.
  • localStorage.clear() : Clear all local storage data.

Notes

  • sessionStorage has the same methods, but the data in sessionStorage is only kept for the duration of the page session

Managing States

CSS classes can be used to define the state of an element

  • <nav className='menu'> : the menu is closed
    • In CSS code: .menu
  • <nav className='menu open'> : the menu is open
    • In CSS code: .menu.open

Managing State of Elements

Use methods like classList.add('open'), classList.remove('open'),classList.toggle('open'), and classList.contains('open') to handle CSS classes

// In Javascript
// Get the element
const leftNav = document.getElementById('leftNav');
// Toggle the menu's state
leftNav.classList.toggle('open'); // add/remove the class 'open' from the element leftNav

Managing State of Whole Site

Use setAttribute(), removeAttribute() and getAttribute() methods alongside localStorage and document.documentElement to manage the whole site states

// In Javascript
// Set the theme to dark and store it
document.documentElement.setAttribute('data-theme', 'dark');
localStorage.setItem('data-theme', 'dark'); 

// Get the current theme
const currentTheme = localStorage.getItem('data-theme') ? localStorage.getItem('data-theme') : null;
if(currentTheme){
    document.documentElement.setAttribute('data-theme', currentTheme);
}

Reloading Sessions

Most browsers automatically save sessions up to a certain point, but for more complicated pages,sessionStorage can be used to save the states of a session

  • sessionStorage data gets deleted when the user closes the tab or window.
  • localStorage and IndexedDB can be used for more persistent data

Restore by pageshow (bfcache)

Sometimes back clicks cannot be detected and all page loads are treated as normal loads. In those cases, this method is more reliable.

<script type='module'>

// Do what is not saved here so it will run every time page loads
getTheme();
loadMenu('mainContainer', 'rightNav');
document.getElementById(loadBtnId).addEventListener('click', () => {
        loadMasonryImages(gridId);
});

// Save gallery and scroll state before leaving page
window.addEventListener('beforeunload', saveGalleryState);

// Save the state of certain elements
function saveGalleryState() {
    const gallery = document.getElementById(gridId);
    sessionStorage.setItem('galleryHTML', gallery.innerHTML);
    sessionStorage.setItem('galleryItemsCount', gallery.childElementCount);
    sessionStorage.setItem('scrollPos', window.scrollY);
}

// Reload what was saved
function restoreGalleryState() {
    const savedHTML = sessionStorage.getItem('galleryHTML');
    if (savedHTML) {
        const gallery = document.getElementById(gridId);
        gallery.innerHTML = savedHTML;
        updateImageCount(sessionStorage.getItem('galleryItemsCount'));
        window.scrollTo(0, sessionStorage.getItem('scrollPos') || 0);
        return true;
    }
    return false;
}

//  What to make once and reload from session if possible
function makeOnce() {
    loadMasonryImages(gridId);
}

// Listen for pageshow (bfcache) and normal load
window.addEventListener('pageshow', (event) => {
    // If restored from bfcache, DOM is already there
    if (event.persisted) {
        console.log('Page restored from bfcache');
        restoreGalleryState();
    } else {
        console.log('Normal page load');
        makeOnce();
    }
});
</script>

Restoring by manually listening for entries event

Use performance.getEntriesByType('navigation')[0].type to get the last navigation method used to enter the page. This is more reliable than the previous method.

window.addEventListener('load', () => {
    const navType = performance.getEntriesByType('navigation')[0].type;
    
    if (navType === 'back_forward') {
        console.log('Back/Forward navigation — restoring gallery');
        restoreGalleryState();
    } else {
        console.log('Normal load — initialize page');
        initPage();
    }
});

Other navigation.type values:

  • 'navigate' : normal load
  • 'reload' : reload
  • 'back_forward' : user navigated via back/forward

addEventListener

addEventListener() method allows the site to listen for when certain events are triggered on the specified element

  • addEventListener(type, listenerFunc)
    • type is the type of event to listen for (e.g., 'click', 'mouseover', 'keyup', 'DOMContentLoaded', 'resize', 'scroll', 'load', 'unload')
    • listenerFunc is the function to be called when the event is triggered
    • Example: menuBtn.addEventListener('click', () => myFunction)

Types of Events

  • Mouse Events
    • click : Mouse click on an element
    • dblclick : Double-click
    • mousedown / mouseup : Mouse button pressed/released
    • mousemove : Mouse moves over an element
    • mouseenter / mouseleave : Mouse enters/leaves an element (no bubbling)
    • mouseover / mouseout : Mouse enters/leaves an element (bubbles)
    • contextmenu : Right-click context menu
  • Keyboard Events
    • keydown : Key is pressed down
    • keypress : Key is pressed (deprecated, use keydown)
    • keyup : Key is released
  • Form & Input Events
    • submit : Form submission
    • change : Input/select value changes
    • input : Input value changes (real-time)
    • focus / blur : Element gains or loses focus
    • focusin / focusout : Similar to focus/blur but bubbles
  • Window / Document Events
    • load : Page or resource fully loaded
    • DOMContentLoaded : DOM fully parsed
    • resize : Window size changes
    • scroll : Scrolling occurs
    • beforeunload / unload : Page is about to leave
  • Drag & Drop Events
    • dragstart, drag, dragend
    • dragenter, dragover, dragleave, drop
  • Clipboard Events
    • copy, cut, paste
  • Touch Events
    • touchstart, touchmove, touchend, touchcancel
  • Media Events
    • play, pause, ended, volumechange, timeupdate
  • Pointer Events
    • pointerdown, pointerup, pointermove, pointerenter, pointerleave, pointercancel
  • Miscellaneous Events
    • contextmenu : Right-click menu
    • wheel : Mouse wheel scroll
    • animationstart, animationend, animationiteration
    • transitionstart, transitionend, transitionrun