diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index b326acd091..da1960835e 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -17,7 +17,7 @@ * under the License. */ -import { isString, indexOf, each, bind, isArray, isDom } from 'zrender/src/core/util'; +import { isString, indexOf, each, bind, isFunction, isArray, isDom } from 'zrender/src/core/util'; import { normalizeEvent } from 'zrender/src/core/event'; import { transformLocalCoord } from 'zrender/src/core/dom'; import env from 'zrender/src/core/env'; @@ -212,14 +212,20 @@ function assembleCssText(tooltipModel: Model, enableTransition?: } // If not able to make, do not modify the input `out`. -function makeStyleCoord(out: number[], zr: ZRenderType, appendToBody: boolean, zrX: number, zrY: number) { +function makeStyleCoord( + out: number[], + zr: ZRenderType, + container: HTMLElement | null | undefined, + zrX: number, + zrY: number +) { const zrPainter = zr && zr.painter; - if (appendToBody) { + if (container) { const zrViewportRoot = zrPainter && zrPainter.getViewportRoot(); if (zrViewportRoot) { // Some APPs might use scale on body, so we support CSS transform here. - transformLocalCoord(out, zrViewportRoot, document.body, zrX, zrY); + transformLocalCoord(out, zrViewportRoot, container, zrX, zrY); } } else { @@ -241,23 +247,22 @@ function makeStyleCoord(out: number[], zr: ZRenderType, appendToBody: boolean, z interface TooltipContentOption { /** - * `false`: the DOM element will be inside the container. Default value. - * `true`: the DOM element will be appended to HTML body, which avoid - * some overflow clip but intrude outside of the container. + * Specify target container of the tooltip element. + * Can either be an HTMLElement, CSS selector string, or a function that returns an HTMLElement. */ - appendToBody: boolean + appendTo: ((chartContainer: HTMLElement) => HTMLElement | undefined | null) | HTMLElement | string } class TooltipHTMLContent { el: HTMLDivElement; - private _container: HTMLElement; + private _api: ExtensionAPI; + private _container: HTMLElement | undefined | null; private _show: boolean = false; private _styleCoord: [number, number, number, number] = [0, 0, 0, 0]; - private _appendToBody: boolean; private _enterable = true; private _zr: ZRenderType; @@ -278,7 +283,6 @@ class TooltipHTMLContent { private _longHideTimeout: number; constructor( - container: HTMLElement, api: ExtensionAPI, opt: TooltipContentOption ) { @@ -291,17 +295,21 @@ class TooltipHTMLContent { (el as any).domBelongToZr = true; this.el = el; const zr = this._zr = api.getZr(); - const appendToBody = this._appendToBody = opt && opt.appendToBody; - makeStyleCoord(this._styleCoord, zr, appendToBody, api.getWidth() / 2, api.getHeight() / 2); + const appendTo = opt.appendTo; + const container: HTMLElement | null | undefined = appendTo && ( + isString(appendTo) + ? document.querySelector(appendTo) + : isDom(appendTo) + ? appendTo + : isFunction(appendTo) && appendTo(api.getDom()) + ); - if (appendToBody) { - document.body.appendChild(el); - } - else { - container.appendChild(el); - } + makeStyleCoord(this._styleCoord, zr, container, api.getWidth() / 2, api.getHeight() / 2); + + (container || api.getDom()).appendChild(el); + this._api = api; this._container = container; // FIXME @@ -350,11 +358,13 @@ class TooltipHTMLContent { update(tooltipModel: Model) { // FIXME // Move this logic to ec main? - const container = this._container; - const position = getComputedStyle(container, 'position'); - const domStyle = container.style; - if (domStyle.position !== 'absolute' && position !== 'absolute') { - domStyle.position = 'relative'; + if (!this._container) { + const container = this._api.getDom(); + const position = getComputedStyle(container, 'position'); + const domStyle = container.style; + if (domStyle.position !== 'absolute' && position !== 'absolute') { + domStyle.position = 'relative'; + } } // move tooltip if chart resized @@ -456,7 +466,7 @@ class TooltipHTMLContent { moveTo(zrX: number, zrY: number) { const styleCoord = this._styleCoord; - makeStyleCoord(styleCoord, this._zr, this._appendToBody, zrX, zrY); + makeStyleCoord(styleCoord, this._zr, this._container, zrX, zrY); if (styleCoord[0] != null && styleCoord[1] != null) { const style = this.el.style; @@ -511,6 +521,7 @@ class TooltipHTMLContent { dispose() { this.el.parentNode.removeChild(this.el); + this.el = this._container = null; } } diff --git a/src/component/tooltip/TooltipModel.ts b/src/component/tooltip/TooltipModel.ts index 413b52fb49..351cc0549b 100644 --- a/src/component/tooltip/TooltipModel.ts +++ b/src/component/tooltip/TooltipModel.ts @@ -61,13 +61,19 @@ export interface TooltipOption extends CommonTooltipOption HTMLElement | undefined | null) | string | HTMLElement + + /** + * Specify the class name of tooltip element * Only available when renderMode is html */ className?: string diff --git a/src/component/tooltip/TooltipView.ts b/src/component/tooltip/TooltipView.ts index 82a222cb1d..5600dc2925 100644 --- a/src/component/tooltip/TooltipView.ts +++ b/src/component/tooltip/TooltipView.ts @@ -171,8 +171,8 @@ class TooltipView extends ComponentView { this._tooltipContent = renderMode === 'richText' ? new TooltipRichContent(api) - : new TooltipHTMLContent(api.getDom(), api, { - appendToBody: tooltipModel.get('appendToBody', true) + : new TooltipHTMLContent(api, { + appendTo: tooltipModel.get('appendToBody', true) ? 'body' : tooltipModel.get('appendTo', true) }); } diff --git a/test/tooltip-appendTo.html b/test/tooltip-appendTo.html new file mode 100644 index 0000000000..644697e17e --- /dev/null +++ b/test/tooltip-appendTo.html @@ -0,0 +1,550 @@ + + + + + + + + + + + + + + + + + + + +

correct position

+
+
+
+
+
+
+ +

3 way to config

+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +