-
-
Notifications
You must be signed in to change notification settings - Fork 540
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DOM support/manipulation #1218
Comments
Hey @r0x0r, I was actually implementing something similar myself. I think this is a great idea! Would it make more sense to have a It would also be great if you could call Syntax wise, an alternative to |
@louisnw01 Thanks for suggestions. I have implemented both Calling These and other changes are pushed to the |
Great solution for As for calling before start, I achieved something similar in my charting library (using pywebview) by creating a However, as your implementation requires an object to be returned to a user-declared variable ( class Element:
def __init__(self, window, node_id) -> None:
self.__window = window
self.events = EventContainer()
self._id = window.generate_unique_id() # window.iudjrnmk
...
def create_element(self, html: str, parent: Optional[Element]=None) -> Element:
parent_selector = parent._query_string if parent else 'document.body'
element = Element(self.__window)
self.run_script(f"""
var parent = {parent_selector};
var template = document.createElement('template');
template.innerHTML = '{escape_quotes(html)}'.trim();
{element._id} = template.content.firstChild;
parent.appendChild({element._id});
pywebview._getNodeId({element._id});
""")
return element This way, the python representations of the DOM elements are not dependant upon objects returned from JavaScript, meaning the scripts can be evaluated in the future (once the window has loaded) by using the |
@louisnw01 Thanks for the suggestion. What about |
More updates on the implementation.
|
The DOM implementation seems to be pretty much complete basic feature wise to me. I take comments / suggestions on the implementation. |
Added
|
Released in 5.0.1 |
The next major version of pywebview (5.0) is going to have support for DOM manipulation and event handling. The idea is to provide a basic set of functions that would allow to manipulate DOM and handle DOM events in Python. Event handling is already implemented in the dom branch. Syntax so far looks like this
You can take a look at a working example at https://github.com/r0x0r/pywebview/blob/dom/examples/dom_events.py
window.get_elements
got refactored so it returns a newElement
object. The underlying DOM node is available viaelement.node
. I am not 100% sure about this syntax, so this might change. Also should properties ofelement.node
be accessible via class dot notation instead of the dict? Ie.element.node.offsetTop
instead ofelement.node['offsetTop']
Window object got a new
window.dom
property that currently hostswindow.dom.document
andwindow.dom.window
(correspond to DOM'swindow.document
andwindow
respectively). These are instances ofElement
, meaning that they follow theelement.node
syntax. Ie to get a window scroll position, you callwindow.dom.window.node['scrollY']
. Would `window.dom.window.scrollY make a better choice?DOM manipulation will provide a basic jQuery like set of functions. hide/show/toggle/remove/next/set_style etc.
Nothing is done on this front yet.Implementation follows a single source of truth principle, ie no data is cached on the Python side, but the latest state is always fetched from DOM .
Please note that these are preliminary ideas and nothing is set it stone. Discussion, ideas and suggestions are welcomed.
So far API changes are as follows
Window
dom
- and instance of aDOM
objectget_elements(selector: str)
returns a list ofElement
objects. Deprecated in favour ofwindow.dom.get_elements
evaluate_js
raises a JavascriptException (serialized Javascript error) if executed code has an error.DOM
body: Element
- an instance of document bodydocument: Element
- an instance ofwindow.document
window: Element
- an instance ofwindow
create_element(html: str, parent: Optional[Element]=None) -> Element
- creates a DOM structure that corresponds to the given html code. Returns anElement
of the root object . If parent is provided, created DOM is attached to the parent as a last child. Otherwise attached to document body.get_element(selector: str) -> Optional[Element])
- get a firstElement
matching the selector. None if not found.get_elements(selector: str) -> list[Element])
- get a list ofElement
matching the selector.webview.dom.Element
node: dict
- jsonified representation of the matching DOM node. Node's children are not included.id: str
- node's id. Get/set propertytag: str
- a tag nametabindex: int
- Get/set tabindex.text: str
- Get/set text content of the elementvalue: any
- Get/set value of the element. Applicable only to input elements that have a valuefocused: bool
- whether element has a focus. Get propertyvisible: bool
- whether element is visible'classes: ClassList' - a list of node's classes. Returns a list like object that can be mutated to update node's classes. Get/set property, accepts an
Iterable
as a setter param.attributes: PropsDict
- Node's attributes. Returns a dict like object that can be mutated to update node's attributes, ie.element.attributes['id'] = 'container'
Get/set property, accepts a dictionary as a setter param. When assigning a new value, new attributes are appended to existing ones overwriting matching attribute keys. Existing non-matching attributes are kept.style: PropsDict
- Node's styles. Get/set property. Works in the same way as attributes.events
- a container class of node's all DOM events. ieevents.click
,event.keydown
etcchildren -> list['Element']
- get a list of node's children. Get property.parent -> Union['Element', None]
- get node's parent or None for the root node . Get property.append(html: str) -> Element
- create and append html as a last childnext -> Union['Element', None]
- get node's next sibling. Get property.previous -> Union['Element', None]
- get node's previous sibling. Get property.hide()
- hide elementshow()
- show elementfocus()
- focus elementblur()
- blur elementtoggle()
- toggle element's visibilitycopy(target: Union[str, 'Element']=None, mode=ManipulationMode.LastChild, id: str=None)
- creates a new copy of the element. If target is omitted, a copy is created in the current element's parent.mode
parameters specifies in which fashion the copy will be inserted to the target. Theid
parameter is stripped from the copy. Optionally you can set a new id by specifying theid
parameter.move(target: Union[str, 'Element'], mode=ManipulationMode.LastChild)
- moves the element to the target container.mode
parameters specifies in which fashion the copy will be inserted to the target.remove()
- remove element from DOM. After element is removed, trying to access any of element's properties/methods results in a warning.empty()
- empty element by removing all its children.on(event: str, callback: Callable)
- attach an event handler to element's DOM event.off(event: str, callback: Callable)
- remove a previously attached event handler from element's DOM event.webview.dom.ManipulationMode enum
Can be used as an argument to
element.copy
andelement.move
to set in which fashion element is moved/copied to another parent. Possible values areLastChild
,FirstChild
,Before
,After
,Replace
webview.dom.DOMEventHandler
DOMEventHandler(callback: Callback, prevent_default=False, stop_propagation=False)
an event handler container used if you need to prevent default behaviour of the catch event or stop event propagation. Example usageelement.events.click += DOMEventHandler(lambda e: e, prevent_default=True, stop_propagation=True)
The text was updated successfully, but these errors were encountered: