Update the content on a webpage dynamically after the page has loaded

Is there a way to dynamically replace text on a webpage, even if it is added or changed by JavaScript in the future? I have checked various solutions in this thread about replacing words in body text, but they only seem to work on existing text at the time of execution.

Answer №1

Discovering the complexity of achieving optimal performance and functionality in HTML, a supposedly declarative markup language, proved to be challenging. Below, I have outlined my discoveries after a month of extensive testing and experimentation.

To begin replacing existing text efficiently, we will utilize TreeWalker to iterate through every Text node in the document and process their content. In this demonstration, we will censor "heck" with "h*ck".

const callback = text => text.replaceAll(/heck/gi, 'h*ck');

function processNodes(root) {
    const nodes = document.createTreeWalker(
        root, NodeFilter.SHOW_TEXT, { acceptNode:
        node => valid(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
    });
    while (nodes.nextNode()) {
        nodes.currentNode.nodeValue = callback(nodes.currentNode.nodeValue);
    }
}

function valid(node) {
    return (
        node.parentNode !== null
        && node.parentNode.tagName !== 'SCRIPT'
        && node.parentNode.tagName !== 'STYLE'
        && !node.parentNode.isContentEditable
    );
}

processNodes(document.body);

Take note of the valid function. This handles three specific cases:

  1. We need to verify that the parent node exists as the node may be removed from the document before processing
  2. Modifying <script> and <style> tags could disrupt functionality or design
  3. Editing a contenteditable element resets the cursor position, leading to a poor user experience

However, the above only covers previously displayed text. To monitor future changes, we can employ MutationObserver to detect newly added or modified text nodes.

const IGNORED = [
    Node.CDATA_SECTION_NODE,
    Node.PROCESSING_INSTRUCTION_NODE,
    Node.COMMENT_NODE,
];
const CONFIG = {subtree: true, childList: true, characterData: true};

const observer = new MutationObserver((mutations, observer) => {    
    observer.disconnect();
    for (const mutation of mutations) {
        const target = mutation.target;
        switch (mutation.type) {
            case 'childList':
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        if (valid(node)) {
                            node.nodeValue = callback(node.nodeValue);
                        }
                    } else if (!IGNORED.includes(node.nodeType)) {
                        processNodes(node);
                    }
                }
                break;
            case 'characterData':
                if (!IGNORED.includes(target.nodeType) && valid(target)) {
                    target.nodeValue = callback(target.nodeValue);
                }
                break;
        }
    }
    observer.observe(document.body, CONFIG);
});
observer.observe(document.body, CONFIG);

The observer's callback features two main sections: one for childList handling new subtrees and text nodes, and another for characterData managing modified text nodes. The observer must be disabled before making any edits to prevent an infinite loop. Additionally, note the IGNORED array, essential for excluding certain non-user-visible nodes falling under the Text interface.

Combining these methods should cover most scenarios effectively. However, several special cases remain unaddressed:

An in-depth explanation of workarounds for these issues is beyond the scope of this StackOverflow response. However, I have developed a free library called TextObserver designed to address them.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Obtaining data from console.log and showcasing it within a div element

Currently, I am facing a challenge where I want to retrieve the title, plot, and poster using the themoviedb API. However, I am lost on how to begin coding this. Whenever I perform a search, the information is displayed in the console log of the browser. ...

What could be the reason for the selection box in my form not changing the items when togg

I'm having an issue with my form selection box code that used to work but is now not functioning properly. Could someone please help me identify where the error lies? Essentially, I have a form with the ID #main and a select box named #chart-type. Th ...

Tips for aligning all elements in-line using Bootstrap CSS

I am trying to make all elements display in a line in my HTML tags. How can I achieve this using Bootstrap? Currently, the page size is displaying on different lines. Thank you, Austin TX <div class="col-xs-12 col-md-8 form-inline"> <span c ...

How to save multiple identification numbers in localStorage with JavaScript

I am looking to implement a favorites feature on my website using HTML, CSS, JavaScript, and JSON. The JSON file is loaded through AJAX and users can search for devices to add to their favorites using sessionStorage and localStorage. However, I'm faci ...

Drag and release: Place within invalid drop areas

I'm currently developing a drag-and-drop web application using Vue.JS and Vuex Store. The drag-and-drop functionality is based on the HTML Drag and Drop API as outlined in the Mozilla documentation. I have successfully implemented the Dropzone Compone ...

Is your Angular2 form page experiencing reloading issues?

I am currently incorporating Angular2 into my project. I am facing an issue where the page keeps refreshing, and I'm unable to determine the cause. Below is a snippet of my form: <form> <div class="form-group"> ...

How can you modify the Bootstrap width to activate the responsive design?

I currently have the following HTML code snippet: <!DOCTYPE html> <html lang="en> <head> <title>Bootstrap Example</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale= ...

How can I navigate and click on a drop-down menu link in Python using Selenium and CSS selectors?

This is an example of the HTML code: <span class="MenuIcons searchButton"></span> ... (additional content) <a data-bind="" url="/ParagonLS/Search/Property.mvc/Index/1" tabdescription="RESIDENTIAL" subtabdescription="Criteria" subtab ...

Telerik: Identifying "button" elements within a dropdown list

I am facing an issue with marking buttons in a RadToolBarDropDown. When using a simple radbutton and changing its checked value by setting CheckOnClick="true", the button is marked with its background changing based on the chosen theme. However, this does ...

Is the fixed div not behaving as expected inside a parent with relative positioning?

Seeking help to troubleshoot an issue I'm facing - I have two divs positioned relatively, with child elements positioned fixed within them. As I scroll the page, these two divs are supposed to become fixed to the top of the browser using the following ...

Is server-side rendering necessary for `browserHistory` in React Router?

I am a beginner in the world of React and currently diving into the complexities of routing. While hashHistory (/#/paths/like/this) proves to be functional, browserHistory (/paths/like/this) appears much cleaner. However, I noticed that when reopening URLs ...

data argument cannot accept an array entry - internal server issue

My attempt to call a C# MVC controller method from a custom JavaScript script using Ajax seems to be encountering an issue with accepting array entries as arguments within the Ajax request. I tested assigning them to non-array variables, which worked, but ...

Maintaining a date attribute for scheduling events

I am seeking advice on a current issue I am facing. Currently, I have a calendar that displays events through one route "/events" in a monthly format. Users can navigate between months by clicking <<< or >>> buttons and view event details ...

Navigating between different route groups using redirection: a step-by-step guide

My project folder structure is organized like this: app (app) dashboard page.tsx page.tsx layout.tsx (auth) login ...

Technique in CSS/SASS to repair a div

Seeking a solution for fixing divs with text in CSS. I am aware of the background-attachment: fixed; property which creates a fancy effect. Is there a similar property to "fix" divs with text or how can this be achieved in Typescript? Your insight would be ...

Leverage ajax to trigger php which then executes python code and retrieves the desired outcome

I've created a website with a complex structure and numerous javascript and php functions. I made the intentional decision to keep it lightweight by avoiding jquery. Now, I want to incorporate a python function that will return a value to the website ...

The browser has disregarded a CSS rule

Hey there, I have a question that may seem silly, but I just can't seem to find the answer anywhere. Please take a look at this fiddle: I need to remove #Navigation in the CSS declaration below: #Navigation .stretch { ... } so it should look like ...

What is the best way to combine limit and fill() in one array?

I am looking to incorporate both limit and fill within the same array. var array = new Array(4).fill({}); var limit = 4; If there are dynamic records, the number may vary but I only need 4 records to display. For example: eg-1 records = 20 It should disp ...

Issue with sourcemaps not mapping correctly in webpack except when the "entry" is specifically indicated

I am currently utilizing ASP.NET Core 2.0. For those interested in viewing the detailed code or running it themselves, it can be accessed here: https://github.com/jakelauer/BaseballTheater/tree/master/BaseballTheaterCore The issue I am encountering is rel ...

The timestamps retrieved from Typeorm's find methods are displaying incorrect times

Currently, I am utilizing express in combination with typeorm and postgresql. A peculiar issue has surfaced - when I save an entity in the database, the timestamp columns are storing the correct UTC Date. However, upon fetching data from the database, ev ...