Find and Replace text in an HTML iframe "srcdoc" attribute using Javascript - javascript

tldr: I'm looking to keep the other text in the srcdoc attribute of an iframe alone, but only swap out the link to the stylesheet using vanilla Javascript.
Longer version:
I'm customizing a Publii blog template and embedding a Cusdis comment widget using their hosted JS SDK.
Publii makes use of HTML, CSS, Javascript, and Handlebars.
The Cusdis widget works by pasting the following code in your html document:
<div id="cusdis_thread"
data-host="https://cusdis.com"
data-app-id="{{ APP_ID }}"
data-page-id="{{ PAGE_ID }}"
data-page-url="{{ PAGE_URL }}"
data-page-title="{{ PAGE_TITLE }}"
>
<script async src="https://cusdis.com/js/cusdis.es.js"></script>
Then, the Cusdis SDK will find the element with id cusdis_thread, then mount the iframe widget on it:
<iframe srcdoc='<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cusdis.com/js/style.css">
<base target="_parent" />
<link>
<script>
window.CUSDIS_LOCALE = undefined
window.__DATA__ = {"host":"https://cusdis.com","appId":"...","pageId":"...","pageUrl":"...","pageTitle":"..."}
</script>
</head>
<body>
<div id="root"></div>
<script src="https://cusdis.com/js/iframe.umd.js" type="module"
</script>
</body>
</html>'
style="width: 100%; border: 0px none; height: 323px;">
#document
</iframe>
My issue is the following:
I want to edit the Cusdis widget's CSS so that the look goes better with my site.
I've tried editing my own stylesheet and selecting Cusdis's CSS classes, but the changes aren't reflected in the output (even with !important). I suspect that it's because the widget generates an iframe, and the elements I want to edit are contained in the iframe.
The workaround seems to be to replace the stylesheet in the iframe's "srcdoc" attribute with a link to another external stylesheet
Because the iframe is automatically generated by Cusdis's SDK, I can't edit that HTML on my end. I'm trying to find a way to replace the stylesheet in the generated iframe's srcdoc using vanilla Javascript.
Here is what I've tried:
Using setAttribute to replace the contents of the attribute:
document.querySelector("#cusdis_thread iframe").setAttribute('srcdoc', '<!DOCTYPE html><html><head><link rel="stylesheet" href="..."><base target="_parent" /><link><script> window.CUSDIS_LOCALE = undefined window.__DATA__ = {"host":"https://cusdis.com", appId":"...","pageId":"{{id}}","pageUrl":"{{url}}","pageTitle":"{{title}}"} </script> </head> <body> <div id="root"></div> <script src="https://cusdis.com/js/iframe.umd.js" type="module"> </script></body></html>');
Result: It worked in theory, but the comment section wasn't generated. When I inspected the code, the attribute's content was replaced. However, I'm using handlebars expressions {{}} to add the PAGE_ID, PAGE_URL, and PAGE_TITLE dynamically, but these expressions are kept inside the srcdoc (so, instead of the iframe displaying the actual URL in the window.__DATA__ =... section, it's still showing the handlebars expression {{url}}).
So, I'm looking for a solution which will keep the other text in the srcdoc attribute alone, but only swap out the link to the stylesheet. Here are my attempts at this:
Using .replace to find the url of Cusdis's stylesheet and replacing it with my own:
document.querySelector("#cusdis_thread iframe").replace("https://cusdis.com/js/style.css", "...");
Result: it was ignored
Using setAttribute for just the stylesheet:
document.querySelector("#cusdis_thread iframe").setAttribute('srcdoc', '<link rel="stylesheet" href="...">');
Result: it was ignored

Since this iframe is loaded through its srcdoc attribute, you can access its inner document from your own document, (because about:srcdoc is magic).
So the best will be to wait for the iframe to load, and to inject your own <link> in there (or modify the StyleSheets as you wish).
const iframe = document.querySelector("iframe");
iframe.addEventListener("load", (evt) => {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "your-stylesheet.css";
iframe.contentDocument.head.append(link);
}, { once: true });
Live Demo (as a jsfiddle because StackSnippets null-origined iframes are dark-magic against same-origin...
The complicated part might be to detect when this iframe is inserted into the document. It seems you already are able to do so, but for the ones who can't, they could either check which event the library fires from, or in worst case scenario, use a MutationObserver.

Related

Changing all absolute URLs anywhere inside the HTML

I have an HTML with the regular tags that looks similar to:
<html>
<head> ... </head>
<body>
...some html....
<script>
window.siteRoot = "https://example.com"
</script>
</body>
</html>
Now inside this HTML, I want to replace all absolute URLs with example.com as the domain to /. How could I do this?
I know I could change the href of the anchor tag by getting them and then altering their href, but here I want to do go beyond the anchor tags and find and replace the absolute URL, which could be anywhere as found in script tag above. How could I do this?
The html element base will help here.
On this example, I added quickly some inline javascript, just for the demo: onmouseover="console.log(this.src)", so we can check the url on mouse over.
But again, it's pure html. It will apply to all relative urls in the document. If you need to avoid it for a particular element, then pass in the full url (<img src="https://otherdomain.com/...")
You will see the src url of the img becoming https://example.com/img.jpg
<html>
<head>
<base href="https://example.com">
</head>
<body>
...some html....
<img src="img.jpg" onmouseover="console.log(this.src)">
<br>
<img src="https://otherdomain.com/remote.png" onmouseover="console.log(this.src)">
</body>
</html>
Note that it can leads to anomalies with scripts and more, it's often a bit hard to use when it's become complicated.
If I understood your question correctly, you could loop the DOM, and search each element with indexOf() for that url, and then change it. Use getElementsByTagName() in a loop, or each one check for the url with indexOf(), and include an if statement - it indexOf() > -1, reassign the url to the new one.

Import entire HTML page into another page

I'm trying to embed another website within my website but I don't want to do it with an iFrame or AJAX import due to some issues that would cause.
<html>
<head>
<link rel="import" href="mysite.html">
</head>
<body>
<script>
var link = document.querySelector('link[rel="import"]');
var content = link.import;
document.body.appendChild(content.cloneNode(true));
</script>
</body>
</html>
Basically I'm trying to do this with HTML Imports from Web Components but unfortunately what I have above does not work (I am using the right browser too) and all the examples I've found were only for importing a specific div or element from within an imported page. But is it possible to simply load the entire page and embed it in another site?
It doesn't work because the object you get from the import property of the <link rel="import"> element is a Document interface.
You cannot insert this type of object inside a <body> element.
Instead you should at least get the <html> element of the imported document from its documentElement property:
var content = link.import.documentElement //returns a html element
But it still incorrect because you will then insert a <html> element inside a <body> element, which is ugly.
You'd rather copy the innerHTML text of the imported document to the main one:
document.body.innerHTML = link.import.querySelector( 'body' ).innerHTML
Or put the HTML you want to import inside a <template>, which is better if you want to defer scripts execution and loadings.

Why doesn't my javascript return a url?

In my script i'm trying to get my Javascript script to return a URL, so I can use the URL as a background for the website.
Here is my code:
//background script
//backgrounds
Rblxscreenshot_zombietower = "http://saberman888etai.net/background_images/rblxscreenshot.png";
Rblxscreenshot_zombietower2 = "http://saberman888.netai.net/background_images/zombietower2.png";
Rblxscreenshot_deathrun = "http://saberman888.netai.net/background_images/deathrun_ice.png";
Rblxscreenshot_deathrun2 = "http://saberman888.netai.net/background_images/deathrun_lobby.png";
SCREENSHOTS = [
Rblxscreenshot_zombietower,
Rblxscreenshot_zombietower2,
Rblxscreenshot_deathrun2,
Rblxscreenshot_deathrun
];
function returnBackground(){
return SCREENSHOTS[Math.floor((Math.random() * SCREENSHOTS.length)+1)];
}
And here is my HTML code:
<!DOCTYPE html>
<head>
<title>Saberman888's Website</title>
<link rel="stylesheet" type="text/css" href="theme.css"/>
<script type="text/javascript" src="background.js"/>
</head>
<body style="background-image:url(<script src="http://saberman888.netai.com/background.js">returnBackground()</script>);">
<div class="box">
<div style="text-align:center;">
<h1>Home</h1>
Home
Conlangs
Projects
</div>
<hr>
<div id="minibox" style="margin-left:100px;">
<h2>Conlangs</h3>
<ul>
<li>Florrum</li>
<li>Genie</li>
</ul>
</div>
<div id="minibox" style="margin-left:100px;">
<h2>Projects</h2>
<ul>
<li>DLBOX</li>
<li>QuarryLang</li>
</ul>
</div>
<div id="links">
My Youtube
My DeviantArt
My Twitter
<a href="8.42.96.39/User.aspx?ID=49027085
">My Roblox</a>
My Github
</div>
</div>
</body>
</html>
As you can see, in the HTML code it uses the function returnBackground() to get a URL to use as a background, but the background doesn't show up, any reason why?
If you try to mod with the length of the array, it will be always inside the range. This issue looks like an out of range error in the line below:
function returnBackground(){
return SCREENSHOTS[Math.floor((Math.random() * SCREENSHOTS.length)+1)];
}
So replace it with:
function returnBackground(){
return SCREENSHOTS[Math.floor((Math.random() * SCREENSHOTS.length)+1) % SCREENSHOTS.length];
}
Update
Just saw a basic mistake, you cannot use a <script> tag or any other tag for that instance, inside an attribute. That's a syntax error:
<body style="background-image:url(<script src="http://saberman888.netai.com/background.js">returnBackground()</script>);">
You cannot set the background URL like that. Instead you need to this way:
<body onload="returnBackground();">
And in the returnBackground() should set the background in this way:
document.body.style.backgroundImage = url;
Your full returnBackground() function:
function returnBackground(){
document.body.style.backgroundImage = SCREENSHOTS[Math.floor((Math.random() * SCREENSHOTS.length)) % SCREENSHOTS.length];
}
The way you're trying to include the script is incorrect.
As per the HTML5 specification, a script tag has to contain either a src attribute or script content inside the tags, not both. (The only allowed content for a script tag with src specified is documentation, i.e. comments.)
Quote on the script element:
If there is a src attribute, the element must be either empty or contain only script documentation that also matches script content restrictions.
(This wasn't correct before HTML5 either, but (I think) it was more ill-defined, so it might work in some browsers, but don't rely on this.)
Also, the script tag cannot be inlined within a style (or any other) attribute.
For example, one of your better options is modifying the script to retrieve the body DOM element and manipulates its style, its background-image specifically (taking a more imperative approach). Then just include this script inside a script tag into your HTML.
Praveen Kumar's suggestion of adding an onload event handler is probably even easier, but the script include has to be fixed regardless of which path you choose.

issue in when loading html file within html using ajax

I loaded a complete html(see below for skeleton) page with the following structure into the another html page using clicking of the button.
<!--START:HTML to be loaded by ajax-->
<head>
<!--START: The content inside this head tag is not processed while the page is loaded via ajax-->
<link rel="stylesheet" type="text/css" href="css/rrr.css" media="screen, projection, print" />
<script>
...
</script>
<script type="text/javascript" src="aaas/xxxx.js"></script>
<!--END: The content inside this head tag is not processed while the page is loaded via ajax-->
</head>
<div id="content">
<!--Page content on which the above script tags inside head tag to act-->
</div>
<!--END:HTML to be loaded by ajax-->
In safari version 5.0.1 and in 5.0.2, the content inside the head tag is not parsed, but the content inside html is parsed in all IE,FF and chrome and safari 5.1.2.
and Content inside div with id equal to "content" is shown in all browsers including safari 5.0.1 and 5.0.2.
Please help me in this.Thanks in advance.
The LINK element can only be used in the HEAD of a document.
From the reference :
Unlike A, it may only appear in the HEAD section of a document,
although it may appear any number of times.
So you may load this inside a iframe (which holds a document) but not inside a div, except for browsers not following the norm.
In your case the easiest workaround would probably be to use an iframe. Note that the css won't apply to the parent document.
Use a documentFragment to store the responseText, then perform the following steps:
Strip out the CSS that should not be applied
Move the script tag into the body
Remove the head tag
Append the result to the parent page

jQuery.html() removes scripts and styles

Is there a way to use jQuery.html() and not loose the scripts and styles? or any other non-jQuery way?
I'm trying to output the full HTML of the page the user is on. Is this even possible?
jQuery.html() removes scripts and styles
That isn't my experience (see below). In practice, you have to be aware that what you get back by querying the DOM for HTML strings is always going to be the interpreted HTML, not the original, and there will be quirks (for instance, on IE all the HTML tag names are IN UPPER CASE). This is because jQuery's html function relies on the browser's innerHTML property, which varies slightly browser to browser. But the demo below includes both style and script tags on Chrome 4, IE7, and Firefox 3.6; I haven't tried others, but I would expect them to be okay.
If you want to get the content of externally-linked pages as well as the inline content, you will naturally have to parse the result and follow the src (on scripts), href (on links that have rel = "stylesheet"), etc...
Demo:
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Test Page</title>
<style type='text/css'>
body {
font-family: sans-serif;
}
</style>
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'></script>
<script type='text/javascript'>
(function() {
$(document).ready(pageInit);
function pageInit() {
$('#btnGo').click(go);
}
function go() {
alert($('html').html());
}
})();
</script>
</head>
<body>
<input type='button' id='btnGo' value='Go'>
</body>
</html>
I see 2 scenarios here
you use jQuery.html(yourHTML) to overwrite the entire html of the page, so even the script tags were overwritten...
you use jQuery.html() to retrieve the entire document html. if this is the case you need tu ensure that the element on which .html() function is used, is the entire html... as T.J. Crowder suggested $('html').html()

Categories