0

Background

I am attempting to read (and eventually change) content within the body of an IFrame (in my example this involves h1 tags) embedded in a Confluence page.

Confluence Behavior

Initially I didn't think it was significant, but as comments by @ScottMarcus illustrate; it does matter that this work is for a possible Confluence plugin. This is because I am using a page decorator containing JavaScript that executes when a user edits a Confluence page.

IFrame Restrictions

This means that (to my knowledge) I cannot have JavaScript execute after the editable IFrame has been added to the DOM. It also means that I cannot control what content is added to the IFrame (although this is OK/expected, as the idea is to enhance the user experience if they add tables and install the plugin).

Example

For reference, here is what the wysiwyg Confluence editor looks like for my page (annotated with a couple key elements from my HTML below):

Confluence Editable IFrame

Problem

Although I have been able to read/log the HTML present within the IFrame, I cannot seem to access elements in the same way I am able to within the main document. I have tried different variations on what I have below, doing things like...

  • selecting elements by ID--for some reason this seems less reliable in this context?
  • trying to get the contents() or children() of elements--in many cases I get null reference exceptions when trying this
  • accessing the body from my IFrame object--again, this doesn't seem the same as doing document.body

JavaScript

function doStuff() {
    $('#wysiwygTextarea_ifr').ready(function () {
        let iFrame = document.getElementById('wysiwygTextarea_ifr');
        let frameDoc = iFrame.contentDocument ? iFrame.contentDocument : iFrame.contentWindow.document;
        let h1Tags = frameDoc.getElementsByTagName('h1');
        //would like to iterate over the collection of h1 tags here, but it always seems to be empty
    });
}

HTML

<!DOCTYPE html>
<html>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
    integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="../Libraries/main.js" type="text/javascript"></script>

<script type="text/javascript">
    doStuff();
</script>

<iframe id="wysiwygTextarea_ifr">
  #document
    <!DOCTYPE html>
    <html>

    <head>
        <title>blah</title>
    </head>

    <body id="tinymce">
        <h1 id="meh">abcde table</h1>
        <h1 id="neh">zzz</h1>
    </body>

    </html>
</iframe>

</html>

  • You are not using [`iframe`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe) correctly. You can't have any content within the opening and closing tags. You must use the `src` attribute to point to another resource. – Scott Marcus Feb 15 '21 at 20:12
  • Also you can use `this` inside `ready()` instead of refetching the `iframe`. – Lain Feb 15 '21 at 20:13
  • And, your `script` elements should be located just before the closing `body` tag so that by the time you run them, all the HTML will have been parsed. Right now, your code executes before the `iframe` has even been reached. – Scott Marcus Feb 15 '21 at 20:15
  • Either use jQuery consistently or not at all. – JavaScript Feb 15 '21 at 20:15
  • @ScottMarcus hah, good to know but I'm not sure I have a say in that (I tried to mimic the Confluence HTML that renders alongside my own the best I could). Where would be the best place to apply the `src` attribute (assuming I have access to that code). Thanks! – phasemaster Feb 15 '21 at 20:31
  • `src` gets added directly to the `iframe` opening tag. – Scott Marcus Feb 15 '21 at 20:38
  • @Lain so if I replace `let iFrame = document.getElementById('wysiwygTextarea_ifr');` with `let iFrame = this` I get `Uncaught TypeError: Cannot read property 'document' of undefined` when the next line executes. Is there an alternate way to get the 'document' inside the frame in this case? Thanks! – phasemaster Feb 15 '21 at 20:41
  • @ScottMarcus You make a good point about script location. But I think the same issue applies because I'm using a 'decorator' page in Confluence which is injected each time a page loads (in other words--I don't think I can control where the IFrame goes). That said, I did try moving the script blocks lower in my code and I get the same result. Thanks. – phasemaster Feb 15 '21 at 20:44
  • @JavaScript lol that's fair. This is what I get for spending hours combing vanilla JavaScript and jQuery solutions :p – phasemaster Feb 15 '21 at 20:45
  • What do you get by logging `frameDoc.body.innerHTML`? – Lain Feb 16 '21 at 07:59
  • @Lain `console.log(frameDoc.body.innerHTML);` appears to give me an empty string (or at least, the result is not undefined). – phasemaster Feb 16 '21 at 14:19
  • The way you predefine markup inside the iframe does not work. [Check this one](https://stackoverflow.com/questions/6102636/html-code-as-iframe-source-rather-than-a-url) – Lain Feb 16 '21 at 16:39
  • @Lain Thanks for the suggestion, but I don't believe I would have the ability to edit the `IFrame` rendered by Confluence. The HTML in my example is essentially a slimmed down version of what ultimately renders what is present in the screenshot I added. In other words, the *user* determines the contents of the IFrame via the wysiwyg GUI. So I am merely trying to "listen" for (and ultimately mutate) certain content (e.g. - `h1` and `table` elements). – phasemaster Feb 16 '21 at 17:42

0 Answers0