Updating a Persistent Window from Javascript Part 2: A Clever Hack
I wrote about
various ways of managing a persistent popup window from Javascript,
eventually settling on a postMessage()
solution that
turned out not to work in QtWebEngine. So I needed another solution.
Data URI
First I tried using a data:
URI.
In that scheme, you encode a page's full content into the URL. For instance:
try this in your browser:
data:text/html,Hello%2C%20World!
So for a longer page, you can do something like:
var htmlhead = '<html>\n' + '<head>\n' + '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">\n' + '<link rel="stylesheet" type="text/css" href="stylesheet.css">\n' + '</head>\n' + '\n' + '<body>\n' + '<div id="mydiv">\n'; var htmltail = '</div>\n' + '</body>\n' + '</html>\n'; var encodedDataURI = encodeURI(htmlhead + noteText + htmltail); var notewin = window.open('data:text/html,' + encodedDataURI, "notewindow", "width=800,height=500");
Nice and easy -- and it even works from file: URIs!
Well, sort of works. It turns out it has a problem related to the same-origin problems I saw with postMessage. A data: URI is always opened with an origin of about:blank; and two about:blank origin pages can't talk to each other.
But I don't need them to talk to each other if I'm not using postMessage, do I? Yes, I do. The problem is that stylesheet I included in htmlhead above:
<link rel="stylesheet" type="text/css" href="stylesheet.css">\n'All browsers I tested refuse to open the stylesheet in the about:blank popup. This seems strange: don't people use stylesheets from other domains fairly often? Maybe it's a behavior special to null (about:blank) origin pages. But in any case, I couldn't find a way to get my data: URI popup to load a stylesheet. So unless I hard-code all the styles I want for the notes page into the Javascript that opens the popup window (and I'd really rather not do that), I can't use data: as a solution.
Clever hack: Use the Same Page, Loaded in a Different Way
That's when I finally came across Remy Sharp's page, Creating popups without HTML files. Remy first explores the data: URI solution, and rejects it because of the cross-origin problem, just as I did. But then he comes up with a clever hack. It's ugly, as he acknowledges ... but it works.
The trick is to create the popup with the URL of the parent page that
created it, but with a named anchor appended:
parentPage.html#popup
.
Then, in the Javascript, check whether #popup
is in the
URL. If not, we're in the parent page and still need to call
window.open
to create the popup. If it is there, then
the JS code is being executed in the popup. In that case, rewrite the
page as needed. In my case, since I want the popup to show only whatever
is in the div named #notes, and the slide content is all inside a div
called #page, I can do this:
function updateNoteWindow() { if (window.location.hash.indexOf('#notes') === -1) { window.open(window.location + '#notes', 'noteWin', 'width=300,height=300'); return; } // If here, it's the popup notes window. // Remove the #page div var pageDiv = document.getElementById("page"); pageDiv.remove(); // and rename the #notes div so it will be displayed in a different place var notesDiv = document.getElementById("notes"); notesDiv.id = "fullnotes"; }
It works great, even in file: URIs, and even in QtWebEngine. That's the solution I ended up using.
[ 19:44 Jan 09, 2020 More tech/web | permalink to this entry | ]