Files
pyscript/docs/guides/event-handlers.md
Mariana Meireles 543a27271f Add docs for py-event* (#1300)
* Fix markdown
Add event-handlers.md

* Address changes from Jeff + Antonion and add it to index

* how tos don't exist anymore theyre now guides

* Restore p on contributing

* Adding changelog

* Aadd space

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: Carmelo <carmelofiorello@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-03-23 17:57:49 +01:00

5.2 KiB

Event handlers in PyScript

PyScript offer two ways to subscribe to Javascript event handlers:

Subscribe to event with py-* attributes

The value of the attribute contains python code which will be executed when the event is fired. A very common pattern is to call a function which does further work, for example:

<button id="noParam" py-click="say_hello_no_param()">
 No Event - No Params py-click
</button>
<button id="withParam" py-click="say_hello_with_param('World')">
 No Event - With Params py-click
</button>
<py-script>
    def say_hello_no_param():
        print("Hello!")

    def say_hello_with_param(name):
        print("Hello " + name + "!")
</py-script>

Note that py-* attributes need a function call

Supported py-* attributes can be seen in the PyScript API reference.

Subscribe to event with addEventListener

You can also subscribe to an event using the addEventListener method of the DOM element. This is useful if you want to pass event object to the event handler.

<button id="two">add_event_listener passes event</button>
<py-script>
  from js import console, document
  from pyodide.ffi.wrappers import add_event_listener

  def hello_args(*args):
    console.log(f"Hi! I got some args! {args}")

  add_event_listener(document.getElementById("two"), "click", hello_args)
</py-script>

or using the addEventListener method of the DOM element:

<button id="three">add_event_listener passes event</button>
<py-script>
  from js import console, document
  from pyodide.ffi import create_proxy

  def hello_args(*args):
    console.log(f"Hi! I got some args! {args}")

    document.getElementById("three").addEventListener("click", create_proxy(hello_args))
</py-script>

or using the PyScript Element class:

<button id="four">add_event_listener passes event</button>
<py-script>
  from js import console
  from pyodide.ffi import create_proxy

  def hello_args(*args):
    console.log(f"Hi! I got some args! {args}")

  Element("four").element.addEventListener("click", create_proxy(hello_args))
</py-script>

JavaScript to PyScript and From PyScript to JavaScript

If you're wondering about how to pass objects from JavaScript to PyScript and/or the other way around head over to the Passing Objects page.

Exporting all Global Python Objects

We can use our new createObject function to "export" the entire Python global object dictionary as a JavaScript object:

<py-script>
    from js import createObject
    from pyodide.ffi import create_proxy
    createObject(create_proxy(globals()), "pyodideGlobals")
</py-script>

This will make all Python global variables available in JavaScript with pyodideGlobals.get('my_variable_name').

(Since PyScript tags evaluate after all JavaScript on the page, we can't just dump a console.log(...) into a <script> tag, since that tag will evaluate before any PyScript has a chance to. We need to delay accessing the Python variable in JavaScript until after the Python code has a chance to run. The following example uses a button with id="do-math" to achieve this, but any method would be valid.)

<py-script>
    # create some Python objects:
    symbols = {'pi': 3.1415926, 'e': 2.7182818}

    def rough_exponential(x):
        return symbols['e']**x

    class Circle():
        def __init__(self, radius):
            self.radius = radius

        @property
        def area:
            return symbols['pi'] * self.radius**2
</py-script>
<input type="button" value="Log Python Variables" id="do-mmath" />
<script>
 document.getElementById("do-math").addEventListener("click", () => {
  const exp = pyodideGlobals.get("rough_exponential");
  console.log("e squared is about ${exp(2)}");
  const c = pyodideGlobals.get("Circle")(4);
  console.log("The area of c is ${c.area}");
 });
</script>

Exporting Individual Python Objects

We can also export individual Python objects to the JavaScript global scope if we wish.

(As above, the following example uses a button to delay the execution of the <script> until after the PyScript has run.)

<py-script>
    import js
    from pyodide.ffi import create_proxy

    # Create 3 python objects
    language = "Python 3"
    animals = ['dog', 'cat', 'bird']
    multiply3 = lambda a, b, c: a * b * c

    # js object can be named the same as Python objects...
    js.createObject(language, "language")

    # ...but don't have to be
    js.createObject(create_proxy(animals), "animals_from_py")

    # functions are objects too, in both Python and Javascript
    js.createObject(create_proxy(multiply3), "multiply")
</py-script>
<input type="button" value="Log Python Variables" id="log-python-variables" />
<script>
 document
  .getElementById("log-python-variables")
  .addEventListener("click", () => {
   console.log(`Nice job using ${language}`);
   for (const animal of animals_from_py) {
    console.log(`Do you like ${animal}s? `);
   }
   console.log(`2 times 3 times 4 is ${multiply(2, 3, 4)}`);
  });
</script>