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