-
Notifications
You must be signed in to change notification settings - Fork 1
Attaching Events Handlers
Attaching event handler to elements is a pretty useful and relevant task. Therefore, it is made very simple in Positron.
Firstly, you need to know which element(s) you want to select. You do this with css selectors. In most cases you will select by id, class or tag.
from positron import *
# Select the element with `id=my-button`
SingleJ("#my-button")
# Select all buttons
J("button")
All the intricacies of CSS selectors will not be covered here.
To attach event handlers to the selected elements we use the beautiful syntax called decorators. In this example we print the id of every button every time it is clicked.
@J("button").on("click")
def _(event: Event):
print(event.target.id)
You can also define how many times your event handler should be called.
In most cases you want to either call the handler the first time or every time an event happens.
For prior there is a special method once
.
In this example, we print the first time a button is hovered over.
@J("button").once("hover")
def _(event: Event):
print(f"Button with id {event.target.id} was hovered over the first time at {event.timestamp}")
For finer control use the repeat
argument.
lives = 10
@J(".dangerous-object").on("hover", repeat=lives)
def _(event: Event):
nonlocal lives
lives -= 1
if lives:
print(f"{lives} lives left. Be careful")
else:
print(f"You died")
The different event types and their attributes are listed in (Events.md). Most of the atributes should never be written to. If you do, the behaviour is undefined - it might blow up the application or nothing might happen.
But there are three boolean flags you can set to a certain value (and only that).
-
cancelled
: If you set this toTrue
and the event iscancellable
then no default actions will be executed -
propagation
: If you set this toFalse
and the event normallybubbles
then no handlers attached to parent elements will be called -
immediate_propagation
: If you set this toFalse
then no further handlers will be called.
cancellable
andbubbles
are web dev terms that have very specific meanings that will not be covered here.
For web devs: These attributes replace
preventDefault()
,stopPropagation()
andstopImmediatePropagation()
You can easily use asynchronous handlers. But be careful - these handlers will not run immediately. Instead, they will be scheduled to run later. This means that setting any of the above mentioned flags might have no effect.
However, in general asynchronous event handlers are pretty cool, if you want to do heavy I/O.
Tip: use
asyncio.to_thread()
to make your synchronous functions run asynchronously.
In this example, we try to interrupt the default action when an anchor is clicked (navigating to the href
) and download the content at the href instead. But this will most likely not work as expected because the event manager doesn't wait for the asynchronous function to finish and executes the default action before the event handler has even run once.
@J("a").on("click")
async def _(event: Event):
event.cancelled = True # This doesn't work
data = await download(event.target.href)
... # do something with the data
Instead do this
async def the_async_function(url):
data = await download(url)
... # do something with the data
@J("a").on("click")
def _(event: Event):
event.cancelled = True # This works
return the_async_function(event.target.href)
This works because we return an awaitable which is almost like defining an asynchronous function in the first place. But the function is run immediately and so the event actually gets cancelled.