You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit d0026fb
Browse filesBrowse the repository at this point in the historyBrowse files
docs(learn): event-handler detection is by callable(), not the key pattern
- responding-to-events: separate_attributes_handlers_and_inline_javascript tests
callable(v) before ever reading the key, so on_click/foo are handlers too;
EVENT_ATTRIBUTE_PATTERN only routes *strings* to inlineJavaScript. A None
handler is an inert attribute, not a serialized handler.
- creating-html-tags: createElementNS lives in the bundled preact chunk, not
@reactpy/client source (grep finds nothing there); stop citing a hash-named
asset and a minified Q symbol nobody can look up.
The bundled client creates every element with`#!python document.createElementNS(namespace, ...)` and picks the namespace from the tag name — `static/index-bz53xswt.js` sets `#!python Q = "http://www.w3.org/2000/svg"` when the type is `#!python "svg"`and the MathML URI for `#!python "math"`. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so `#!python viewBox` keeps its capital `#!python B` — write it that way, since `#!python viewbox` is a different attribute to the DOM.
204
+
The bundled client creates every element through`#!js document.createElementNS(namespace, ...)`, picking the namespace from the tag name: `#!python "svg"` gets `http://www.w3.org/2000/svg`, `#!python "math"`gets the MathML URI, everything else the XHTML one. That logic lives in the preact runtime ReactPy bundles into its client chunks, not in ReactPy's own `#!js @reactpy/client` source — so grep there for `createElementNS` and you will find nothing. Inside the SVG namespace attribute names are forwarded verbatim rather than lower-cased, so `#!python viewBox` keeps its capital `#!python B` — write it that way, since `#!python viewbox` is a different attribute to the DOM.
Writing `#!python html.button({"on_click": handle_click}, ...)` does **not** raise. ReactPy accepts any `#!python on*` key as a handler and ships it to the browser verbatim — but no browser event is named `#!python on_click`, so the button looks fine and simply never fires. Only the names *you* define (variables, components, component props) are `#!python snake_case`.
71
+
Writing `#!python html.button({"on_click": handle_click}, ...)` does **not** raise. ReactPy ships it to the browser verbatim as a handler for an event called `#!python _click` — the client registers a listener for the key with the leading `#!js on` stripped — and no browser ever fires one. The button looks fine and simply never responds. Only the names *you* define (variables, components, component props) are `#!python snake_case`.
72
72
73
-
ReactPy recognises a key as an event handler when it matches `#!python ^on[A-Z]\w+` (`core/vdom.py`, `EVENT_ATTRIBUTE_PATTERN`).
73
+
Note what does and does not decide "this is a handler". In `core/vdom.py`, `separate_attributes_handlers_and_inline_javascript` the test is **`callable(v)` on the value**, checked before the key is ever looked at: a callable under `#!python "on_click"`, or even under `#!python "foo"`, lands in `#!python eventHandlers`. The key pattern `#!python ^on[A-Z]\w+` (`core/vdom.py`, `EVENT_ATTRIBUTE_PATTERN`) is used only for the *string* case below — it decides whether a string becomes `#!python inlineJavaScript` or a plain attribute. So `#!python {"onClick": "alert(1)"}` runs as JavaScript, while `#!python {"onclick": "alert(1)"}` and `#!python {"on_click": "alert(1)"}` become inert attributes.
74
74
75
75
Alternatively, you can define an event handler inline with a `#!python lambda`:
76
76
@@ -102,7 +102,7 @@ Inline event handlers are convenient for short functions.
102
102
html.button({"onClick": handle_click()}, "...") # wrong: fires during render
103
103
```
104
104
105
-
In the second case the `#!python ()` calls `#!python handle_click` _immediately_ during [rendering](./render-and-commit.md), without any clicks — because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is then stored as the handler, which is usually `#!python None`. ReactPy does not reject that: `#!python None` is serialised as a handler that never runs, so the button renders and does nothing, forever.
105
+
In the second case the `#!python ()` calls `#!python handle_click` _immediately_ during [rendering](./render-and-commit.md), without any clicks — because the Python inside the call is evaluated right away, exactly like an expression inside JSX braces. Whatever it returns is what gets stored, and a function that ends without a `#!python return` gives `#!python None`. ReactPy does not reject that: handlers are picked out by `#!python callable(v)`, and `#!python None` is not callable, so it falls through to the plain attributes and ships as `#!python {"onClick": None}` — a DOM attribute named `onClick` holding `null`. Nothing registers, nothing fires, and the button renders and does nothing, forever.
0 commit comments