How to change the HTML content as it's loading on the page
The docs on MDN have a generic incomplete example and don't showcase the common pitfalls.
Mutation summary library provides a human-friendly wrapper, but like all wrappers it adds overhead.
See Performance of MutationObserver to detect nodes in entire DOM.
Create and start the observer.
Let's use a recursive document-wide MutationObserver that reports all added/removed nodes.
var observer = new MutationObserver(onMutation);
observer.observe(document, {
childList: true, // report added/removed nodes
subtree: true, // observe any descendant elements
});
Naive enumeration of added nodes.
Slows down loading of enormously big/complex pages, see Performance.
Sometimes misses the H1 elements coalesced in parent container, see the next section.
function onMutation(mutations) {
mutations.forEach(mutation, m => {
[...m.addedNodes]
.filter(node =>
node.localName === 'h1' && /foo/.test(node.textContent))
.forEach(h1 => {
h1.innerHTML = h1.innerHTML.replace(/foo/, 'bar');
});
});
}
Efficient enumeration of added nodes.
Now the hard part. Nodes in a mutation record may be containers while a page is being loaded (like the entire site header block with all its elements reported as just one added node): the specification doesn't require each added node to be listed individually, so we'll have to look inside each element using querySelectorAll
(extremely slow) or getElementsByTagName
(extremely fast).
function onMutation(mutations) {
for (var i = 0, len = mutations.length; i < len; i++) {
var added = mutations[i].addedNodes;
for (var j = 0, node; (node = added[j]); j++) {
if (node.localName === 'h1') {
if (/foo/.test(node.textContent)) {
replaceText(node);
}
} else if (node.firstElementChild) {
for (const h1 of node.getElementsByTagName('h1')) {
if (/foo/.test(h1.textContent)) {
replaceText(h1);
}
}
}
}
}
}
function replaceText(el) {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
for (let node; (node = walker.nextNode());) {
const text = node.nodeValue;
const newText = text.replace(/foo/, 'bar');
if (text !== newText) {
node.nodeValue = newText;
}
}
}
Why the two ugly vanilla for
loops? Because forEach
and filter
and ES2015 for (val of array)
could be very slow in some browsers, see Performance of MutationObserver to detect nodes in entire DOM.
Why the TreeWalker? To preserve any event listeners attached to sub-elements. To change only the Text
nodes: they don't have child nodes, and changing them doesn't trigger a new mutation because we've used childList: true
, not characterData: true
.
Processing relatively rare elements via live HTMLCollection without enumerating mutations.
So we look for an element that is supposed to be used rarely like H1 tag, or IFRAME, etc. In this case we can simplify and speed up the observer callback with an automatically updated HTMLCollection returned by getElementsByTagName.
const h1s = document.getElementsByTagName('h1');
function onMutation(mutations) {
if (mutations.length === 1) {
// optimize the most frequent scenario: one element is added/removed
const added = mutations[0].addedNodes[0];
if (!added || (added.localName !== 'h1' && !added.firstElementChild)) {
// so nothing was added or non-H1 with no child elements
return;
}
}
// H1 is supposed to be used rarely so there'll be just a few elements
for (var i = 0, h1; (h1 = h1s[i]); i++) {
if (/foo/.test(h1.textContent)) {
// reusing replaceText from the above fragment of code
replaceText(h1);
}
}
}
How to change html content, when the page has fully loaded
You're calling the function in the setup for your "load" event.
Did you mean:
window.addEventListener("load", MyFunction);
??
How to edit a website's HTML and then have it be there when you reload but only for you?
The easiest way to do something like this would be to install a userscript manager like Tampermonkey. Then you can create a userscript for the site that changes the HTML to how you want it to be, and (if you've written the code properly) it'll automatically run every time you load the site.
For example, due to a bug in Stack Exchange's CSS/Javascript, quickly double-clicking on a snippet when it's loading results in errors, so I currently have the following userscript to fix it:
// ==UserScript==
// @name Stack Snippet Modal Fixer
// @description Prevents snippet double-clicking from breaking the snippet interface
// @author CertainPerformance
// @version 1.0.0
// @include /^https://(?:(?:(?:codereview|gamedev|codegolf|meta)\.)(?:[^/]+\.)?stackexchange\.com|(?:[^/]+\.)?stackoverflow\.com)/(?:questions/(?:\d|ask/)|posts/\d+/edit|review/(?:reopen|helper|low-quality-posts|suggested-edits)(?:/\d+|$))/
// @grant none
// ==/UserScript==
document.body.appendChild(document.createElement('style')).textContent = `
.snippet-modal {
pointer-events: auto !important;
}
`;
This uses Javascript to append a <style>
tag to the document, but you can make whatever other changes you want to the document as well (like changing HTML of a page, or removing style rules of an existing inline <style>
, etc).
The only limits to a userscript are the limitations of Javascript on a page, but most things one would want to tweak can probably be achieved with Javascript.
Personally, I would have a hard time browsing many of the sites I frequent without the ability to write userscripts to customize sub-optimal interfaces.
Change HTML code after page load (w/ jQuery ?)
You forgot to add ')' to your Javascript.
but i really cant realize what you are trying to do here.
any way
$(document).ready(function () { // my RegEx works well, verified it on regex101
let col_let_num = $('body').html().replace(/\bcol\b(\-[a-z]{0,2})?(\-)?([0-9]{0,2})?/i, 'col-12')
$('body').html(col_let_num)
})
Edited
here you go
$('[class*="col"]').each((i, e) => {
let classes = $(e).attr('class').split(/\s+/);
classes.forEach(v => {
let col_let_num = v.replace(/\bcol\b(\-[a-z]{0,2})?(\-)?([0-9]{0,2})?/i, 'col-12')
$(e).attr('class', col_let_num)
})
})
this should work.
Change HTML code without refreshing the page
Using jQuery
(a JavaScript library) you can utilize the load()
function to load the contents of another HTML file on your server and place it anywhere you want on the current page without refreshing (so you can even replace the current HTML if you like).
jQuery:
http://jquery.com/
jQuery load():
http://api.jquery.com/load/
Alternative Suggestion:
However, I know you say you can't refresh the page, but, if the only reason is because you need to keep the text in the textbox, you could use a form to POST
that text to another .jsp
page (or even the same .jsp
page, depending on how you go about it) where it will be available to you to use at your own discretion (to put it in another textbox, for example).
Related Topics
How Would You Overload the [] Operator in JavaScript
Firebase.Database Is Not a Function
JavaScript Search Array of Arrays
Youtube Iframe Player API - Onstatechange Not Firing
Access Elements of Parent Window from Iframe
React - User-Defined Jsx Components Not Rendering
Creating Range in JavaScript - Strange Syntax
Ruby on Rails 4 JavaScript Not Executed
How to Format a Utc Date as a 'Yyyy-Mm-Dd Hh:Mm:Ss' String Using Nodejs
In JavaScript How Do I/Should I Use Async/Await with Xmlhttprequest
Linguistic Meaning of 'Let' Variable in Programming
Google Maps Places API V3 Autocomplete - Select First Option on Enter
JavaScript Parse Float Is Ignoring the Decimals After My Comma
Triggering Onclick Event Using Middle Click