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

Cannot copy text from custom widget output. #5444

Closed
vthemelis opened this issue May 12, 2020 · 2 comments
Closed

Cannot copy text from custom widget output. #5444

vthemelis opened this issue May 12, 2020 · 2 comments

Comments

@vthemelis
Copy link

vthemelis commented May 12, 2020

Hello,

I followed the instructions on https://ipywidgets.readthedocs.io/en/stable/examples/Widget%20Custom.html in order to create a new custom widget.

I noticed that it is not possible to copy the text output of the resulting widget. When highlighting the text (refer to attached picture) and pressing Ctrl-C, it is the input of the cell that is copied rather than the output.

What causes this issue? Are there any known workarounds?

image

In the picture above, I selected the email text and once I copied, I expected the paste to be "[email protected]". Instead, the paste is "Email()"

Edit:
Note that copy/paste work fine with some text HTML tags like h1, but the same problem appears if I wrap them with a shadow root:
image

@adamreeve
Copy link

I can reproduce this problem in Google Chrome 81.0.4044.138 on Windows 10 with Python 3.8.2, Jupyter notebook server 6.0.3 and IPython 7.14.0. However, I can’t reproduce the issue with Firefox 76.0.1 or Edge 18363.

Digging into this further, it looks like the copy event handler registered in clipboard.js is never called when the copy event is from within a disabled input element or inside a shadow root when using Firefox. With Chrome, the event handler is called but the selection returned by window.getSelection has isCollapsed set to true, even though the selection type is "Range" and there is text selected. This means that the copy function in clipboard.js doesn’t return early when it should:

if ((Jupyter.notebook.mode !== 'command') ||
// window.getSelection checks if text is selected, e.g. in output
!window.getSelection().isCollapsed ||
// Allow programmatic copy
isProgrammaticCopy(event)) {
return;
}

The selection object viewed in Chrome dev-tools looks like:

{
    anchorNode: div.p-Widget
    anchorOffset: 0
    baseNode: div.p-Widget
    baseOffset: 0
    extentNode: div.p-Widget
    extentOffset: 0
    focusNode: div.p-Widget
    focusOffset: 0
    isCollapsed: true
    rangeCount: 1
    type: "Range"
}

It looks to me like for the copy function to work correctly in Chrome, it would make more sense to check for window.getSelection().type == 'Range' rather than !window.getSelection().isCollapsed.

To work around this, I can add an event handler on the widget to prevent the Jupyter event handler from running:

function copyFromWidget(event) {
    // Don't allow Jupyter to handle the copy event,
    // but allow the default handler to run.
    event.stopPropagation();
}

define('email_widget', ["@jupyter-widgets/base"], function(widgets) {

    var EmailView = widgets.DOMWidgetView.extend({

        // Render the view.
        render: function() {
            this.email_input = document.createElement('input');
            this.email_input.type = 'email';
            this.email_input.value = '[email protected]';
            this.email_input.disabled = true;
            
            this.el.appendChild(this.email_input);
            
            this.el.addEventListener('copy', copyFromWidget);
        },
    });
    
    return {
        EmailView: EmailView
    };
});

@adamreeve
Copy link

Some further details after digging into this some more:

Firefox not bubbling the copy event out of the shadow DOM is a bug (https://bugzilla.mozilla.org/show_bug.cgi?id=1620420). The copy event not bubbling out of an input element also looks like a bug in Firefox although there isn't an existing bug report for this that I can find.

My suggested fix of checking the selection type would work for Chrome, but in Firefox window.getSelection doesn't work with input elements. MDN says:

It is worth noting that currently getSelection() doesn't work on the content of <textarea> and elements in Firefox, Edge (Legacy) and Internet Explorer. HTMLInputElement.setSelectionRange() or the selectionStart and selectionEnd properties could be used to work around this.

When I test this I get either a selection with type = 'None' or previously selected text rather than a selection from within the input element. Checking for selection type = 'Range' would be fine for now but would break in Firefox when they fix the copy event not bubbling.

When selecting within a shadow DOM, Firefox does expose the details of the selection from window.getSelection but this appears to be incorrect and Chrome is behaving correctly by not exposing the shadow DOM details. There's work going on in WICG/webcomponents#79 to decide how selection APIs should work across shadow DOMs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants