Skip to content
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

Provide ActiveHTMLWidget that allows users to properly interact with HTML as rendered in the DOM #3433

Open
b3m2a1 opened this issue Apr 9, 2022 · 5 comments

Comments

@b3m2a1
Copy link

b3m2a1 commented Apr 9, 2022

Problem

Across my few weeks using it, ipywidgets has been consistently added unnecessary restrictions to my desired workflow.

At the end of the day, I'd really just like ipywidgets to provide a proxy by which I can interact with the DOM, since JupyterLab is best for prototyping which means I'd like to be able to iterate fast and not need to churn out random custom widgets for every possible case of anything I'd ever like to do with my research code.

I can generate HTML in python no problem. I can generate very nice HTML in python no problem. I can make it look and be laid out however I might want, which is something I certainly cannot easily do with ipywidgets (trust me, I tried).

At the end of the day, I really just want ipywidgets to be my tunnel between my python code and the DOM. That's it. Given what I want to do (and what I'm guessing many researchers want to do based on the comments in this thread: jupyterlab/jupyterlab#5660) I think the path of least resistance all around would be to have an extension to the basic HTML widget that does more than just allow one to set the innerHTML attribute and instead allows one to query parts of the actual live DOM element generated.

This would solve 100% of my issues with ipywidgets by allowing me to create my layouts and interactions myself, never needing to try to cobble together 80 different custom widgets to create the kind of layout that HTML & CSS supports naturally.

Proposed Solution

I think it's overkill to ask you all to support the entire DOM API out of the box, but I can imagine a few small changes that would allow this to work much better:

  1. Provide access to the childNodes attribute of an HTML element. These could be represented easily as some kind of NodeProxy that simply describes that relation between the child node and the element created by the ActiveHTMLWidget. The element itself could have an empty path as the proxy element.
  2. Provide access to a small number of attributes, maybe even just the classList, style, and value attributes. With these, 95% of web-related stuff can be managed and creating some kind of tunnel to pass just them along would be easy.
  3. Optionally, integrate interactivity through the approach taken by the ipyevents extension. It's a phenomenal extension for getting around the general lack of rich interactions in the base ipywidgets suite.
@b3m2a1
Copy link
Author

b3m2a1 commented Apr 11, 2022

A functioning implementation of this idea is here: https://github.com/b3m2a1/ActiveHTMLWidget

It attempts to provide a tunnel to the DOM and then get out of the way.

It currently supports attributes, styles, events, using non-div tags, and borrows code from ipywidgets.Box to allow for embedding other widgets inside the widget.

A fun minimal example:

dump = ipywidgets.Output()
def log(*e):
    with dump:
        print(*e)
header = HTMLElement(innerHTML="<h1>Input Field with Event Listeners</h1>", classList=["card-header"])
button = HTMLElement(tagName="button", textContent="????", classList=['input-group-text'], eventPropertiesDict={'click':['button', 'shiftKey']})
button.bind_callback(log)
input_group = HTMLElement(
    children=[
        button,
        HTMLElement(tagName="input", classList=["form-control"], elementAttributes={'placeholder':'input'}, trackInput=True)
    ],
    classList=['input-group', 'mb-3']
)
body = HTMLElement(
    children=[
        input_group,
        HTMLElement(tagName="div", classList=["alert", "alert-info"], textContent="Event output is printed below"),
        HTMLElement(tagName="hr"),
        dump
    ], 
    classList=['card-body']
)
HTMLElement(children=[header, body], classList=["card"])

Screen Shot 2022-04-11 at 3 55 52 PM

@b3m2a1
Copy link
Author

b3m2a1 commented May 1, 2022

An initial peek at where this idea has gone: https://b3m2a1.github.io/jhtml-a-web-framework-for-jupyter#body

@vidartf
Copy link
Member

vidartf commented Aug 23, 2022

@b3m2a1 Thanks for creating a library for this, and for sharing! Your original post included some potential improvements to ipywidgets to make it easier to write such a third party widget. With the experience of having written that library, are there any of those points that still remain? If so, maybe you can open separate issues and/or PRs for them?

@b3m2a1
Copy link
Author

b3m2a1 commented Aug 31, 2022

@vidartf Unfortunately it's been so long since I wrote the widget library I've forgotten, but I think the biggest complaint was that the documentation makes it seem like the dev environment needs to be clunkier than it is (and I think I had to write my own scripts to install the widget to avoid expensive jupyter widgets rebuilds or something)

@saulshanabrook
Copy link

For anyone coming to this page, AnyWidget might be a good replacement for this library. It lets you render arbitrary JS with a widget, allowing you to set custom HTML if you like easily.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants