Creating a Diagram in a non-local window

I have a reference to a window object which hosts the div element for which I would liek to construct a GOJS Diagram object.

var myWindowRef = window.open('my.page.html')

When I try to pass in the div without any reference to the parent window I get the following error:

Error: Invalid DIV id; could not get the element with id: myID

So then I tried to make a reference to my div in the following fashion

var myDivRef = myWindowRef.document.getElementBYID('myID')

And passing that reference into the

go.GraphObject.make(go.Diagram, myDivRef, ...)

statement.

However this resulted in the following error:

Error: Trying to set undefined property "align" on object: Diagram ""

Is this a futile endeavor? Thank you!

It is true that there is no Diagram.align property.

Perhaps you want to get your code working on a regular Div element on your page before you try it in a different window.

I face the same issue. In my case, I use IFrames to display diagrams, so document reference, which GoJS uses is not correct, I give a HTMLDivElement to the constructor, like the author of this topic did, but GoJS raises the same error, even though diagram works perfectly fine in a local-window.

The problem ultimately lays with a check for node instanceof HTMLDivElement in the GoJS source code, in the go.GraphObject.make and few other functions (I found at least 3 of these checks in the newest version). The library is not aware in which context it is being used and what DOM node is given to it.

As described in this stackoverflow answer https://stackoverflow.com/a/5724974 and also in https://stackoverflow.com/a/42950193 answer comments, instanceof might give unexpected result if it is called from a different scope. In my case I initialize go.Diagram from a main window, but the DOM, in which diagram is created - in an IFrame. So at the end node instanceof HTMLDivElement just always gives false and because of it, then the function tries to read parameter as an object, which then leads to trying to read align attribute on the HTMLDivElement node https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement and assigning it to the Diagram object.

To prevent instanceof giving false assumptions, I would suggest to change this check to an alternative, which would not be influenced by the window context being something else than expected.

There are at least 3 different alternatives:

    div instanceof HTMLDivElement

    1. div.toString() === '[object HTMLDivElement]'
    2. div.tagName === "DIV"
    3. div.nodeName === "DIV"

I’ve in a hacky way replaced instanceof comparison in GoJs source with the first alternative, to check if that would solve the issue, and it did, no errors were raised from GoJS library. Everything was working as expected. Obviously, I wouldn’t like to do hacking (because of obvious reasons) and I would like, that GoJS support this kind of scenario. The recommended way I guess would be to use tagName. Would this be possible to change?

I’m attaching a fiddle in which error can be seen (open up dev tools console): https://jsfiddle.net/Lbroqyud/

Thanks, we’ll consider changing something here for the next release.

Is there a reason @ovigri that you can’t put the relevant code itself in the iFrame? I’m concerned that there may be similar issues beyond testing for HTMLDivElement.

Actually, here’s my recommendation. You have this code:

const html = `<div id="diagram" class="diagram"></div>`;
frameDoc.body.insertAdjacentHTML("beforeend", html);

This creates a <div> in the iFrame’s window context (using the iFrame’s HTMLDivElement and not window.HTMLDivElement), even though GoJS is going to populate that DIV with elements created via the root window’s document (for instance the <canvas> inside the DIV). If you want the outside window to control the JS for this frame, it would be more consistent to create the DIV outside the frame too.

So I suggest you replace the above code with this:

const div = document.createElement('div');
div.id = "diagram";
div.className = "diagram";
frameDoc.body.appendChild(div);

And it should work fine, with no changes to the library needed.

(Another alternative would be to put all code inside the iFrame, but there may be reasons why you do not wish to do that.)

Thanks for reply. There is a really special case here, why we are doing this way. We are implementing an extension for JupyterLab and we already have the code base, so it was an idea to reuse the code instead of rewriting everything. We have a lot of HTML involved and it is very time consuming to now make it all created through JS as elements, instead of inserting HTML text straight to DOM. Another point is that JupyterLab extension is running in the main window and we want to control all diagrams from it, so that’s why the initialization is happening from within main window.

We want to have variable amount of diagrams in the same window, so it was necessary to either have all HTML ID’s unique (which would again, take a lot of time to replace in the code base and HTML, because with diagram there a lot of other tools involved) or have an isolated environment like IFrame’s. So IFrame’s looked like a way to go.

Anyway, even if we make it work using IFrame’s like in your recommendation, problems does not end there… Because of how IFrame’s work in a browser, main window does not catch particular events from within an IFrame, so GoJS just does not function properly, for example, dragging, scrolling does not work.

Having in mind all these problems, we concluded to just make all ID’s unique in HTML, even if it would take some time refactoring the code base.

Thank you for recommendations and answers :)