How to use react-tether with react-dom createPortal to be able to style tethered component according to the...
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}
I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.
The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.
This is the idea I already came up with, but it doesn't work as it is.
export const Bar: React.StatelessComponent<IBarProps> = (props) => {
const { button1, button2} = props;
return (
<div>
<TetherComponent
...
>
<Button
ref={button1ref} //???
...(some props)
>
{button1.text}
</Button>
{
createPortal(
<tooltip>button1.tooltipText</tooltip>,
button1ref)
}
</TetherComponent>
<TetherComponent
...
>...the same with second button
</TetherComponent>
</div>
)}
where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.
I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
I'm open to any idea.
javascript reactjs typescript jsx
add a comment |
I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.
The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.
This is the idea I already came up with, but it doesn't work as it is.
export const Bar: React.StatelessComponent<IBarProps> = (props) => {
const { button1, button2} = props;
return (
<div>
<TetherComponent
...
>
<Button
ref={button1ref} //???
...(some props)
>
{button1.text}
</Button>
{
createPortal(
<tooltip>button1.tooltipText</tooltip>,
button1ref)
}
</TetherComponent>
<TetherComponent
...
>...the same with second button
</TetherComponent>
</div>
)}
where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.
I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
I'm open to any idea.
javascript reactjs typescript jsx
add a comment |
I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.
The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.
This is the idea I already came up with, but it doesn't work as it is.
export const Bar: React.StatelessComponent<IBarProps> = (props) => {
const { button1, button2} = props;
return (
<div>
<TetherComponent
...
>
<Button
ref={button1ref} //???
...(some props)
>
{button1.text}
</Button>
{
createPortal(
<tooltip>button1.tooltipText</tooltip>,
button1ref)
}
</TetherComponent>
<TetherComponent
...
>...the same with second button
</TetherComponent>
</div>
)}
where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.
I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
I'm open to any idea.
javascript reactjs typescript jsx
I have a Component in which I render some buttons. Each button should have its tooltip. I'd like to make tooltip appear when I :hover button. But because my tooltip can contain not only text, I cannot use things like balloon or create it only with css.
The idea is to use react-tether to tether my own Tooltip component to the button. But then I have to manage that if I hover the button, the tooltip appears. So I want to use react-portal to move the tooltip inside of my button component so I'd be able to style the tooltip according to its actual parent.
This is the idea I already came up with, but it doesn't work as it is.
export const Bar: React.StatelessComponent<IBarProps> = (props) => {
const { button1, button2} = props;
return (
<div>
<TetherComponent
...
>
<Button
ref={button1ref} //???
...(some props)
>
{button1.text}
</Button>
{
createPortal(
<tooltip>button1.tooltipText</tooltip>,
button1ref)
}
</TetherComponent>
<TetherComponent
...
>...the same with second button
</TetherComponent>
</div>
)}
where Tooltip is just React.StatelessComponent and Button is simple React.PureComponent.
I get usually stuck at the problem, that Button is JSX.Element and not Element so I cannot use it as a targeted element in the portal. Another thing is, that I am not sure about how to use ref in this case and if it's not better to use id.
I'm open to any idea.
javascript reactjs typescript jsx
javascript reactjs typescript jsx
asked Jan 3 at 16:12


Linda LangerováLinda Langerová
366
366
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
I think this peace of code I wrote will help you.
It doesn't work exactly like tooltip, but it shows how you can use portal.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
render() {
const tooltip = (
<div>
{this.props.tooltip}
</div>
);
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.buttonRef.current) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.
It doesn't have any sense to open portal to the same room :)
I think you need to do something like this:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
constructor() {
super();
this.tooltipElement = document.createElement('div');
}
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
componentDidMount() {
document.body.appendChild(this.tooltipElement);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
let buttonRect;
let tooltip;
if (this.buttonRef.current) {
buttonRect = this.buttonRef.current.getBoundingClientRect();
tooltip = (
<div style={{
zIndex: Number.MAX_SAFE_INTEGER,
position: 'absolute',
top: buttonRect.top,
left: buttonRect.right,
backgroundColor: 'lightgrey'
}}>
{this.props.tooltip}
</div>
);
}
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.tooltipElement) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54025981%2fhow-to-use-react-tether-with-react-dom-createportal-to-be-able-to-style-tethered%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I think this peace of code I wrote will help you.
It doesn't work exactly like tooltip, but it shows how you can use portal.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
render() {
const tooltip = (
<div>
{this.props.tooltip}
</div>
);
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.buttonRef.current) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.
It doesn't have any sense to open portal to the same room :)
I think you need to do something like this:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
constructor() {
super();
this.tooltipElement = document.createElement('div');
}
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
componentDidMount() {
document.body.appendChild(this.tooltipElement);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
let buttonRect;
let tooltip;
if (this.buttonRef.current) {
buttonRect = this.buttonRef.current.getBoundingClientRect();
tooltip = (
<div style={{
zIndex: Number.MAX_SAFE_INTEGER,
position: 'absolute',
top: buttonRect.top,
left: buttonRect.right,
backgroundColor: 'lightgrey'
}}>
{this.props.tooltip}
</div>
);
}
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.tooltipElement) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
add a comment |
I think this peace of code I wrote will help you.
It doesn't work exactly like tooltip, but it shows how you can use portal.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
render() {
const tooltip = (
<div>
{this.props.tooltip}
</div>
);
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.buttonRef.current) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.
It doesn't have any sense to open portal to the same room :)
I think you need to do something like this:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
constructor() {
super();
this.tooltipElement = document.createElement('div');
}
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
componentDidMount() {
document.body.appendChild(this.tooltipElement);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
let buttonRect;
let tooltip;
if (this.buttonRef.current) {
buttonRect = this.buttonRef.current.getBoundingClientRect();
tooltip = (
<div style={{
zIndex: Number.MAX_SAFE_INTEGER,
position: 'absolute',
top: buttonRect.top,
left: buttonRect.right,
backgroundColor: 'lightgrey'
}}>
{this.props.tooltip}
</div>
);
}
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.tooltipElement) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
add a comment |
I think this peace of code I wrote will help you.
It doesn't work exactly like tooltip, but it shows how you can use portal.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
render() {
const tooltip = (
<div>
{this.props.tooltip}
</div>
);
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.buttonRef.current) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.
It doesn't have any sense to open portal to the same room :)
I think you need to do something like this:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
constructor() {
super();
this.tooltipElement = document.createElement('div');
}
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
componentDidMount() {
document.body.appendChild(this.tooltipElement);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
let buttonRect;
let tooltip;
if (this.buttonRef.current) {
buttonRect = this.buttonRef.current.getBoundingClientRect();
tooltip = (
<div style={{
zIndex: Number.MAX_SAFE_INTEGER,
position: 'absolute',
top: buttonRect.top,
left: buttonRect.right,
backgroundColor: 'lightgrey'
}}>
{this.props.tooltip}
</div>
);
}
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.tooltipElement) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
I think this peace of code I wrote will help you.
It doesn't work exactly like tooltip, but it shows how you can use portal.
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
render() {
const tooltip = (
<div>
{this.props.tooltip}
</div>
);
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.buttonRef.current) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
This example shows how to use portal, but the purpose of the portal is to add something (tooltip element) to the another element (document.body for example) not to the same element. It this case createPortal doesn't actually do anything.
It doesn't have any sense to open portal to the same room :)
I think you need to do something like this:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
class TooltipButton extends Component {
state = {
isHovered: false
};
buttonRef = React.createRef();
constructor() {
super();
this.tooltipElement = document.createElement('div');
}
onMouseOver = isOver => {
this.setState({
isHovered: isOver
});
};
componentDidMount() {
document.body.appendChild(this.tooltipElement);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
let buttonRect;
let tooltip;
if (this.buttonRef.current) {
buttonRect = this.buttonRef.current.getBoundingClientRect();
tooltip = (
<div style={{
zIndex: Number.MAX_SAFE_INTEGER,
position: 'absolute',
top: buttonRect.top,
left: buttonRect.right,
backgroundColor: 'lightgrey'
}}>
{this.props.tooltip}
</div>
);
}
return (
<div>
<button
ref={this.buttonRef}
onMouseEnter={() => this.onMouseOver(true)}
onMouseLeave={() => this.onMouseOver(false)}
>
{this.props.text}
</button>
{this.state.isHovered ?
ReactDOM.createPortal(tooltip, this.tooltipElement) :
undefined}
</div>
);
}
}
TooltipButton.propTypes = {
text: PropTypes.string.isRequired,
tooltip: PropTypes.string.isRequired
};
export default TooltipButton;
edited Jan 3 at 19:20
answered Jan 3 at 18:18
ValeriiValerii
9932620
9932620
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
add a comment |
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
Thanks for your advise :) I guess my problem is, that making portal actually makes sense, because I use react-tether that adds the tethered component to the body, not as a child of targeted element.
– Linda Langerová
Jan 7 at 15:03
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54025981%2fhow-to-use-react-tether-with-react-dom-createportal-to-be-able-to-style-tethered%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown