@@ -253,3 +253,237 @@ paragraphs.style['background-color'] = 'lightyellow'
253
253
a collection by setting the proper CSS rules, using ` style ` with the same API as a dictionary.
254
254
* ` html ` : allows to change the ` html ` attribute on all the elements of a collection.
255
255
* ` value ` : allows to change the ` value ` attribute on all the elements of a collection.
256
+
257
+ ## Working with JavaScript
258
+
259
+ There are three ways in which JavaScript can get into a web page.
260
+
261
+ 1 . As a global reference attached to the ` window ` object in the web page
262
+ because the code was referenced as the source of a ` script ` tag in your HTML
263
+ (the very old school way to do this).
264
+ 2 . Using the [ Universal Module Definition] ( https://github.com/umdjs/umd ) (UMD)
265
+ - an out-of-date and non-standard way to create JavaScript modules.
266
+ 3 . As a standard
267
+ [ JavaScript Module] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules )
268
+ which is the modern, standards compliant way to define and use a JavaScript
269
+ module. If possible, this is the way you should do things.
270
+
271
+ Sadly, this being the messy world of the web, methods 1 and 2 are still quite
272
+ common, and so you need to know about them so you're able to discern and work
273
+ around them. There's nothing WE can do about this situation, but we can
274
+ suggest "best practice" ways to work around each situation.
275
+
276
+ Remember, as mentioned
277
+ [ elsewhere in our documentation] ( ../configuration/#javascript-modules ) ,
278
+ the standard way to get JavaScript modules into your PyScript Python context is
279
+ to link a _ source_ standard JavaScript module to a _ destination_ name:
280
+
281
+ ``` toml title="Reference a JavaScript module in the configuration."
282
+ [js_modules .main ]
283
+ "https://cdn.jsdelivr.net/npm/[email protected] /dist/leaflet-src.esm.js" =
" leaflet"
284
+ ```
285
+
286
+ Then, reference the module via the destination name in your Python code, by
287
+ importing it from the ` pyscript.js_modules ` namespace:
288
+
289
+ ``` python title="Import the JavaScript module into Python"
290
+ from pyscript.js_modules import leaflet as L
291
+
292
+ map = L.map(" map" )
293
+
294
+ # etc....
295
+ ```
296
+
297
+ We'll deal with each of the potential JavaScript related situations in turn:
298
+
299
+ ### JavaScript as a global reference
300
+
301
+ In this situation, you have some JavaScript code that just globally defines
302
+ "stuff" in the context of your web page via a ` script ` tag. Your HTML will
303
+ contain something like this:
304
+
305
+ ``` html title="JavaScript as a global reference"
306
+ <!doctype html>
307
+ <!--
308
+ This JS utility escapes and unescapes HTML chars. It adds an "html" object to
309
+ the global context.
310
+ -->
311
+ <
script src =
" https://cdn.jsdelivr.net/npm/[email protected] /index.js" ></
script >
312
+
313
+ <!--
314
+ Vanilla JS just to check the expected object is in the global context of the
315
+ web page.
316
+ -->
317
+ <script >
318
+ console .log (html);
319
+ </script >
320
+ ```
321
+
322
+ When you find yourself in this situation, simply use the ` window ` object in
323
+ your Python code (found in the ` pyscript ` namespace) to interact with the
324
+ resulting JavaScript objects:
325
+
326
+ ``` python title="Python interaction with the JavaScript global reference"
327
+ from pyscript import window, document
328
+
329
+
330
+ # The window object is the global context of your web page.
331
+ html = window.html
332
+
333
+ # Just use the object "as usual"...
334
+ # e.g. show escaped HTML in the body: <>
335
+ document.body.append(html.escape(" <>" ))
336
+ ```
337
+
338
+ You can find an example of this technique here:
339
+
340
+ [ https://pyscript.com/@agiammarchi/floral-glade/v1 ] ( https://pyscript.com/@agiammarchi/floral-glade/v1 )
341
+
342
+ ### JavaScript as a non-standard UMD module
343
+
344
+ Sadly, these sorts of non-standard JavaScript modules are still quite
345
+ prevalent. But the good news is there are strategies you can use to help you
346
+ get them to work properly.
347
+
348
+ The non-standard UMD approach tries to check for ` export ` and ` module ` fields
349
+ in the JavaScript module and, if it doesn’t find them, falls back to treating
350
+ the module in the same way as a global reference described above.
351
+
352
+ If you find you have a UMD JavaScript module, there are services online to
353
+ automagically convert it to the modern and standards compliant way to d
354
+ o JavaScript modules. A common (and usually reliable) service is provided by
355
+ [ https://esm.run/your-module-name ] ( https://esm.run/your-module-name ) , a
356
+ service that provides an out of the box way to consume the module in the
357
+ correct and standard manner:
358
+
359
+ ``` html title="Use esm.run to automatically convert a non-standard UMD module"
360
+ <!doctype html>
361
+ <script type =" module" >
362
+ // this utility escapes and unescape HTML chars
363
+ import { escape , unescape } from " https://esm.run/html-escape" ;
364
+ // esm.run returns a module ^^^^^^^^^^^^^^^^^^^^^^^^^^^
365
+ console .log (escape (" <>" ));
366
+ // log: "<>"
367
+ </script >
368
+ ```
369
+
370
+ If a similar test works for the module you want to use, use the esm.run CDN
371
+ service within the ` py ` or ` mpy ` configuration file as explained at the start
372
+ of this section on JavaScript (i.e. you'll use it via the ` pyscript.js_modules `
373
+ namespace).
374
+
375
+ If this doesn't work, assume the module is not updated nor migrated to a state
376
+ that can be automatically translated by services like esm.run. You could try an
377
+ alternative (more modern) JavaScript module to achieve you ends or (if it
378
+ really must be this module), you can wrap it in a new JavaScript module that
379
+ conforms to the modern standards.
380
+
381
+ The following four files demonstrate this approach:
382
+
383
+ ``` html title="index.html - still grab the script so it appears as a global reference."
384
+ <!doctype html>
385
+ ...
386
+ <!-- land the utility still globally as generic script -->
387
+ <
script src =
" https://cdn.jsdelivr.net/npm/[email protected] /index.js" ></
script >
388
+ ...
389
+ ```
390
+
391
+ ``` js title="wrapper.js - this grabs the JavaScript functionality from the global context and wraps it (exports it) in the modern standards compliant manner."
392
+ // get all utilities needed from the global.
393
+ const { escape , unescape } = globalThis .html ;
394
+
395
+ // export utilities like a standards compliant module would do.
396
+ export { escape , unescape };
397
+ ```
398
+
399
+ ``` toml title="pyscript.toml - configure your JS modules as before, but use your wrapper instead of the original module."
400
+ [js_modules .main ]
401
+ # will simulate a standard JS module
402
+ "./wrapper.js" = " html_escaper"
403
+ ```
404
+
405
+ ``` python title="main.py - just import the module as usual and make use of it."
406
+ from pyscript import document
407
+
408
+ # import the module either via
409
+ from pyscript.js_modules import html_escaper
410
+ # or via
411
+ from pyscript.js_modules.html_escaper import escape, unescape
412
+
413
+ # show on body: <>
414
+ document.body.append(html.escape(" <>" ))
415
+ ```
416
+
417
+ You can see this approach in action here:
418
+
419
+ [ https://pyscript.com/@agiammarchi/floral-glade/v2 ] ( https://pyscript.com/@agiammarchi/floral-glade/v2 )
420
+
421
+ ### A standard JavaScript module
422
+
423
+ This is both the easiest and best way to import any standard JS module into
424
+ Python.
425
+
426
+ You don't need to reference the script in your HTML, just define how the source
427
+ JavaScript module maps into the ` pyscript.js_modules ` namespace in your
428
+ configuration file, as explained above.
429
+
430
+ That's it!
431
+
432
+ Here is an example project that uses this approach:
433
+
434
+ [ https://pyscript.com/@agiammarchi/floral-glade/v3 ] ( https://pyscript.com/@agiammarchi/floral-glade/v3 )
435
+
436
+
437
+ ### My own JavaScript code
438
+
439
+ If you have your own JavaScript work, just remember to write it as a standard
440
+ JavaScript module. Put simply, ensure you ` export ` the things you need to. For
441
+ instance, in the following fragment of JavaScript, the two functions are
442
+ exported from the module:
443
+
444
+ ``` js title="code.js - containing two functions exported as capabilities of the module."
445
+ /*
446
+ Some simple JavaScript functions for example purposes.
447
+ */
448
+
449
+ export function hello (name ) {
450
+ return " Hello " + name;
451
+ }
452
+
453
+ export function fibonacci (n ) {
454
+ if (n == 1 ) return 0 ;
455
+ if (n == 2 ) return 1 ;
456
+ return fibonacci (n - 1 ) + fibonacci (n - 2 );
457
+ }
458
+ ```
459
+
460
+ Next, just reference this module in the usual way in your TOML or JSON
461
+ configuration file:
462
+
463
+ ``` TOML title="pyscript.toml - references the code.js module so it will appear as the code module in the pyscript.js_modules namespace."
464
+ [js_modules .main ]
465
+ " code.js" = " code"
466
+ ```
467
+
468
+ In your HTML, reference your Python script with this configuration file:
469
+
470
+ ``` html title="Reference the expected configuration file."
471
+ <script type =" py" src =" ./main.py" config =" ./pyscript.toml" terminal ></script >
472
+ ```
473
+
474
+ Finally, just use your JavaScript module’s exported functions inside PyScript:
475
+
476
+ ``` python title="Just call your bespoke JavaScript code from Python."
477
+ from pyscript.js_modules import code
478
+
479
+
480
+ # Just use the JS code from Python "as usual".
481
+ greeting = code.hello(" Chris" )
482
+ print (greeting)
483
+ result = code.fibonacci(12 )
484
+ print (result)
485
+ ```
486
+
487
+ You can see this in action in the following example project:
488
+
489
+ [ https://pyscript.com/@ntoll/howto-javascript/latest ] ( https://pyscript.com/@ntoll/howto-javascript/latest )
0 commit comments