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

Plotly.js is incompatible with Chrome's native shadow DOM implementation #1433

Closed
myw opened this issue Mar 2, 2017 · 13 comments
Closed

Plotly.js is incompatible with Chrome's native shadow DOM implementation #1433

myw opened this issue Mar 2, 2017 · 13 comments

Comments

@myw
Copy link

myw commented Mar 2, 2017

Context

I develop and maintain ginkgobioworks/plotly-plot, a Polymer element that encapsulates plotly.js. Polymer is a web-component framework written by Google, which uses browser functionality known as the "shadow DOM" to isolate the internals of web components from the rest of the page.

Polymer 1.x has two kinds of shadow DOM implementations: native shadow DOM (v0), and a
shim called "shady DOM." Native shadow DOM is newer and yields improved
performance, but it has poor support in browsers outside of Chrome
and can cause problems with existing code. For this reason, shady DOM is
still the default implementation in Polymer 1.x.

Issue

Unfortunately, native shadow DOM is currently incompatible with plotly.js. The
icon toolbar layout code in the plotly.js library fails for all plotly plots
rendered inside a shadow DOM.

Example

Since it's a pain to create your own elements without the tools in a code pen, the easiest example to observe is on the plotly-plot demo page.

You can render the page either with shady DOM (the default) or with shadow DOM by passing in the ?dom=shadow GET parameter, which forces Polymer to use native shadow DOM v0 in browsers that support it.

Expected/Observed

Under shady DOM (no GET parameter), the elements behave as expected. Under shadow DOM (with the GET parameter), however, the toolbars fail to render entirely.

Impact

I don't know why this is the case. It might have to do with SVG support for the buttons, or CSS effects.

The work-around on my side is that anyone who consumes my element must not turn on native shadow DOM rendering on any page that renders a plotly-plot widget. This is fine for small, simple pages, but can be a real drag when front-end performance is at stake.

I'm wondering if there might be a relatively easy fix that would prevent this from happening—if there is, it'd be nice to do it. If not, it's probably not worth doing any serious rewriting for this use case.

In any case, thought you'd be interested to know.

@etpinard
Copy link
Contributor

etpinard commented Mar 2, 2017

Thanks very much for the report. Nice work with plotly-plot !

the toolbars fail to render entirely.

Just to clarify, by toolbars you're referring to the plotly modebar?

If so, I suspect a similar issue to #702

@etpinard
Copy link
Contributor

etpinard commented May 8, 2017

Closing due to lack of activity.

@etpinard etpinard closed this as completed May 8, 2017
@myw
Copy link
Author

myw commented May 8, 2017

@etpinard whoops—didn't see your responses. Yes, I did mean the modebar, and yes, the behavior is just like #702. I'm fine with this being closed relative to that issue, if resolving #702 also fixes this bug. (Although, tbh, this particular case might be more of a Shadow DOM/Polymer issue than a plotly issue, since turning on Shadow DOM ought to have no effect.)

Also, thank you! 😊

Cheers!

@paolobeltrame
Copy link

I have the same problem with the legend.
I'm using Polymer 1.0 and Polymer 2.0. They have by default native Shadow DOM v1 turned on if the browser supports it. And the result is like this

schermata 2017-05-09 alle 10 03 00

In Polymer 1.0 you can turn off native shadow DOM and everything works fine but If you plan to use Plotly with native shadow DOM v1 toolbar and legend are not working as expected.

I think this can have a huge impact for who wants to use Plotly + web components since Shadow DOM v1 has became W3C spec.

@skortchmark9
Copy link

I am also experiencing this issue - it seems like it makes it impossible to use plotly with polymer and perhaps even native webcomponents. I attempted to use the child-window branch in #702 but it did not fix the problem.

@jdfergason
Copy link

I believe the problem is in the way that plotly adds style to the document. When the plotly.js script is loaded it initializes itself by running the following function for a number of css styles:

lib.addStyleRule = function(selector, styleString) {
    if(!lib.styleSheet) {
        var style = document.createElement('style');
        // WebKit hack :(
        style.appendChild(document.createTextNode(''));
        document.head.appendChild(style);
        lib.styleSheet = style.sheet;
    }
    var styleSheet = lib.styleSheet;

    if(styleSheet.insertRule) {
        styleSheet.insertRule(selector + '{' + styleString + '}', 0);
    }
    else if(styleSheet.addRule) {
        styleSheet.addRule(selector, styleString, 0);
    }
    else lib.warn('addStyleRule failed');
};

This appends a new style tag to the head of the document. Chrome doesn't seem to totally understand this because if you try to inspect the element it just appears empty. This is not a problem if you are not using a shadowRoot as the styles are still correctly applied. However, in a shadowRoot the style encapsulation screws things up and the styles are not found.

You can get around this by adding the required styles to your template style section. This is kind of a mess but it does work. I have created an example project that shows the initial problem and the hack working at: plotly-polymer-fix

There is a script at happybase that claims to be a cross-browser way to create css styles dynamically. I replaced the original implementation of addStyleRule with this one and it also does not work.

@skortchmark9
Copy link

skortchmark9 commented Jul 4, 2017

I've also found that the following css restores proper behavior in my polymer component:

    /* plotly require those class to render correctly */
    .js-plotly-plot .plotly .main-svg {
        position: absolute;
        top: 0;
        left: 0;
        pointer-events: none;
    }

EDIT: actually never mind, it's just slightly better, and jdfergason's styles are much more complete. However, there are still some issues with getting the position of the mouse for the zoom / hover actions.

bergie added a commit to c-base/infoscreens that referenced this issue Nov 30, 2017
mkaul added a commit to ccmjs/mkaul-components that referenced this issue Oct 14, 2018
@darkengines
Copy link

This is because css must be defined inside the component's shadow dom.
Just generate the final css and do what you need with it:

sass .\node_modules\plotly.js\src\css\style.scss > plolty.css

Then do what you want with css, if you are using lit-element your can expose the css this way:

import { css } from 'lit-element';

const plotlyStyle = css`
	.js-plotly-plot .plotly,
	.js-plotly-plot .plotly div {
		direction: ltr;
		font-family: 'Open Sans', verdana, arial, sans-serif;
		margin: 0;
		padding: 0;
	}
        ........
        *** css from generated plotly.css file ***
        ........
	.plotly-notifier .notifier-close:hover {
		color: #444;
		text-decoration: none;
		cursor: pointer;
	}
`;
export default plotlyStyle;

Then you are free to import this piece of css in your individual components.

@tyler-albert
Copy link

tyler-albert commented Feb 1, 2021

I discovered a hacky approach to getting full Plotly support inside of shadow dom that people might find useful until better shadow dom support is available. Basically, if you put a slot inside of the shadow dom and then slot in the plot div into the custom element containing that shadow dom, the plot will behave as if it's in the light dom but will be visually located inside the shadow dom. I've made a demo project demonstrating this trick using the polymer3 starter project:

Plotly Shadow DOM Slot Hack

I myself was having issues getting plotly_click and plotly_hover events to fire and solved it this way, but this solution is meant to answer the broader topic of Plotly being incompatible with shadow dom.

The downside is that you have to slot things in from the top level of the dom structure, but it does work. Also, this approach should work for any framework, as long as slots are an option.

@LeUser111
Copy link

LeUser111 commented Jul 9, 2021

For anyone using Lit and running into the same problem: Overriding createRenderRoot and returning this tells lit-element to render its content in the light-dom. This fixes the shadow dom issues and amazingly even seems to work when the element itself is deeply nested inside shadow roots of other elements :)

Example (in scala.js):

final class Chart() extends LitElement {

	override protected def createRenderRoot(): EventTarget = this

	override protected def render(): TemplateResult =
		html"""
		<div></div>
		"""

	override protected def firstUpdated(changedProperties: js.Map[String, js.Object]): Unit = {
		super.firstUpdated(changedProperties)
		val data = ...   
		val div = querySelector("div").asInstanceOf[HTMLElement]
		Plotly.plot(div, data)
	}

}

Edit: Grammar

@TotallyInformation
Copy link

This is still an issue with Web Components as I've just found out.

Seems unlikely that Plotly will ever be very useful as browsers and w3c standards progress, plotly's assumptions about its environment are increasingly unsustainable. Rather sad since some effort has been made to support browser ECMA module use.

@leopsidom
Copy link

leopsidom commented Jan 7, 2024

@darkengines solution does work. Is there a way we can get the global css required via the distribution package: https://www.npmjs.com/package/plotly.js-dist

@echus
Copy link

echus commented Oct 12, 2024

+1, the best workaround would be providing compiled CSS in the dist package. I'm also running into this issue with web components in SolidJS currently.

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