@@ -68,6 +68,10 @@ equivalent values: `["hello", 1, 2, 3]`.
6868 instantiation very differently. By explicitly calling the JavaScript
6969 class's `new` method PyScript both signals and honours this difference.
7070
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 ) .
7175
7276## PyDom
7377
@@ -253,3 +257,237 @@ paragraphs.style['background-color'] = 'lightyellow'
253257a collection by setting the proper CSS rules, using ` style ` with the same API as a dictionary.
254258* ` html ` : allows to change the ` html ` attribute on all the elements of a collection.
255259* ` 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