Trouble linking dom libraries


#1

I’m getting this error, which references a line number (ln 77) in the dom library, so my guess is that my html found the dom file, but I can’t figure out why that file can’t find p5. I currently have p5.js in a different folder from p5.dom.js; could this be the problem?

The error message:
TypeError: p5 is undefined p5.dom.js:77:3

My code works fine if I remove any (very few) references to dom elements, but I’d like to add a slider and ideally access the rest of the dom related stuff, so please help!

UPDATE

What I’m actually trying to do is load my scripts dynamically. I want to pick a random js file and run that as my script. That works fine for just p5js but for some reason it breaks when I try to load the dom this way too. Linking these files statically works fine like this:

<script src="../js/p5js/p5.js" type="text/javascript"></script>
<script src="../js/p5js/addons/p5.dom.js" type="text/javascript"></script>
<script src="../js/script.js" type="text/javascript"></script>

But I want to be able to vary the path to my js file arbitrarily (like a random choice from a hardcoded array of path strings). This seems to cause one of two problems. Here’s what I do with just p5 and my script:

<script>function initjs(){
    // load p5js
    var p5js = document.createElement("script");
    p5js.src = "../js/p5js/p5.js";
    p5js.type = "text/javascript";
    document.getElementsByTagName('head')[0].appendChild(p5js);

    // load script
    var script = document.createElement("script");
    script.src = "../js/" + jsfiles[Math.floor(Math.random()*jsfiles.length)];
    script.type = "text/javascript";
    document.getElementsByTagName('head')[0].appendChild(script);
}</script>
...
<body onload="initjs();">

And this works just fine. The problem occurs when I try to add the dom to this.

<script>function initjs(){
    // load p5js
    ...
    
    // load p5domjs
    var dom = document.createElement("script");
    dom.src = "../js/p5js/addons/p5.dom.js";
    dom.type = "text/javascript";
    document.getElementsByTagName('head')[0].appendChild(dom);
    
    // load script
    ...
}</script>
...
<body onload="initjs();">

This seems like it would work because the other combination worked, but it fails giving the error from above. My assumption is that it is taking too long for either the dom or p5 to load and therefore not all variables could be initialized. Taking a look at line 77 in p5.dom.js, which gave this error:

TypeError: p5 is undefined p5.dom.js:77:3

The dom is trying to reference a variable `p5’ but I don’t make that so I assume that is part of the p5.js file. Anyway, in order to fix this I added a couple timeouts to try to give them enough time to initialize and the errors went away:

function loadp5() {
        // load p5js
        var p5js = document.createElement("script");
        p5js.onload = function(){setTimeout(loaddom, 50);};     <!--- added these --->
        p5js.src = "../js/p5js/p5.js";
        p5js.type = "text/javascript";
        document.getElementsByTagName('head')[0].appendChild(p5js);
}
function loaddom() {
        // load p5domjs
        var dom = document.createElement("script");
        dom.onload = function(){setTimeout(loadscript,50);};     <!--- added these --->
        dom.src = "../js/p5js/addons/p5.dom.js";
        dom.type = "text/javascript";
        document.getElementsByTagName('head')[0].appendChild(dom);
}
function loadscript() {
        // load a random js file
        var script = document.createElement("script");
        script.src = "../js/" + jsfiles[Math.floor(Math.random()*jsfiles.length)];
        script.type = "text/javascript";
        document.getElementsByTagName('head')[0].appendChild(script);
}
...
<body onload="loadp5();">

As I said, the errors in the console went away but now my script is never called! I put console.log calls in the setup() and draw() functions of script.js and in loadp5(), loaddom(), and loadscript(). Only the logs from the load functions appear so my script is not getting program control for some reason. I’m sure I’m doing something wrong, so please help! and yes I know I can just do the static loading and use a single js file for my script but that would lead to ugly code because I want to have a ton of options for the random script.

THANKS


#2

These two links look promising:

https://stackoverflow.com/questions/32847752/javascript-dynamic-script-loading-via-functions-how-to-do-onload-or-readystat
https://codereview.stackexchange.com/questions/97629/html-and-js-to-dynamically-load-refresh-and-execute-javascript-from-external-f

Kf


#3

Okay, after looking at this one:

I figured I’d just verify that p5 is defined before even trying to load the dom (because I didn’t want to manually edit the dom files because I think that’s bad practice if I can avoid it). Anyway, here’s what I tried:

// load p5js
...

while(typeof p5 === "undefined"){}       <!--- never exits --->

//load dom
...

//load script
...

The code blocks at the while loop, even if I leave out the dom code altogether. But this is interesting. If I put the following in setup() of my script and link the js files staticly, false is printed and the code works fine:

console.log(typeof p5 === "undefined");

So is the problem the scope of the p5 variable?? I don’t think I know enough about javascript scoping to figure this out. Based on the success of this line, I will try to write a separate dispatch.js file that will hopefully have the scope of p5, and load the dom from there. I will update… Please let me know if you have any other ideas of what is going wrong!


#4

Okay, I figured it out. It was a matter of combining the static and dynamic loading methods. I noticed that the `p5’ variable was available in the scope of the statically loaded files, so within the initjs() onload callback the dom couldn’t get access to it (at least I think that was the problem).

With that in mind, the fix is to make a statically loaded file `dispatch.js’ that does the dynamic loading. So my html head looks like this:

<head>    
    <script src="../js/p5js/p5.js" type="text/javascript"></script>
    <script src="../js/p5js/addons/p5.dom.js" type="text/javascript"></script>
    <script src="../js/dispatch.js" type="text/javascript"></script>
</head>

…and dispatch.js looks like this:

var jsfiles = ["script.js", ... ];
function initjs(){
    // load script
    var script = document.createElement("script");
    script.src = "../js/"+jsfiles[Math.floor(Math.random()*jsfiles.length)];
    script.type = "text/javascript";
    document.getElementsByTagName("head")[0].appendChild(script);
}
initjs();

Even though I got it working, I’m still not exactly sure why this works, so if someone with more experience could fill me in that’d be swell!


#5

No need to load a script dynamically in order to modify the class p5.
JS allows us to edit properties on-the-fly: :cowboy_hat_face:


#6

yes, I wasn’t saying loading them dynamically was the solution; rather, it was the problem because p5.dom.js couldn’t see ‘p5’ in the first place. Loading dynamically was so that I could choose a random script file. The solution was to split up the dynamic and static loading such that p5.dom.js could still access ‘p5’ during loading and I could still choose an arbitrary js file later.


#7

The “p5.dom.js” is in itself a patch for the “p5.js” file.
Actually, all libraries made for p5js patch themselves into the class p5.
Just make sure the “p5.js” file is loaded before any patches of it!