ipywidgets icon indicating copy to clipboard operation
ipywidgets copied to clipboard

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

Open b3m2a1 opened this issue 3 years ago • 5 comments

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: https://github.com/jupyterlab/jupyterlab/issues/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 avatar Apr 09 '22 07:04 b3m2a1

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 avatar Apr 11 '22 22:04 b3m2a1

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

b3m2a1 avatar May 01 '22 02:05 b3m2a1

@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?

vidartf avatar Aug 23 '22 13:08 vidartf

@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)

b3m2a1 avatar Aug 31 '22 20:08 b3m2a1

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.

saulshanabrook avatar Oct 10 '23 14:10 saulshanabrook