'React, offsetTop,offsetLeft and getBoundingClientRect() are not working

I'm working in react and I'm creating a button with a tooltiop, but I can't place it. I mean I can't read the button's distance from the top and left.

I tried offsetTop and offsetLeft and I got this error: "Uncaught TypeError: Cannot read property 'offsetWidth' of undefined" so I tried getBoundingClientRect() and all I got is another error: "Uncaught TypeError: elem.getBoundingClientRect is not a function".

I'm passing the information from component to the second component by assigning the distances to the global variable and read it in this second component when I need to place it.

This is my code(on stage when I'm trying to do sth with getBoundingClientRect):

import React from 'react';
import ReactDOM from 'react-dom';
import Style from 'style-it'; 
var Ink = require('react-ink');
import FontIcon from '../FontIcon/FontIcon';

var options;

var Tooltip = React.createClass({
  render(){

    var _props = this.props,
      style = {
          top: options.y,
          left: options.x,
      };

    return(
      <div className="tooltip" style={style}>{_props.text}</div>
    );
  }
});

var IconButton = React.createClass({ 

  getInitialState() {
      return {
          iconStyle: "",
          style: "",
          cursorPos: {},
      };
  },

  extend(obj, src) {
      Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
      return obj;
  },

  render() {

    var _props = this.props,
      opts = {},
      disabled = false,
      rippleOpacity,
        outterStyleMy = {
        border: "none",
            outline: "none",
            padding: "8px 10px",
        "background-color": "red",
        "border-radius": 100 + "%",
        cursor: "pointer",
        },
        iconStyleMy = {
            "font-size": 12 + "px",
            "text-decoration": "none",
            "text-align": "center",
            display: 'flex',
            'justify-content': 'center',
            'align-items': 'center',
        },
      rippleStyle = {
        color: "rgba(0,0,0,0.5)",
      };

    if (_props.disabled || _props.disableTouchRipple) {
      rippleStyle.opacity = 0;
    };

    this.setState({
      iconStyle: _props.iconStyle
    });

    this.setState({
      style: _props.style
    });

    if (_props.disabled) {
       disabled = true;
    };

    if (this.state.labelStyle) {
        iconStyleMy = this.state.iconStyle;
    };

    if (this.state.style) {
      outterStyleMy = this.state.style;
    };

    if (_props.href) {
      opts.href = _props.href;
    };

    function showTooltip(elem){
      // Here I tried to use offset methods, it's how it looked like:
      // options = {
      //   w: this.refs.button.offsetWidth,
      //   x: this.refs.button.offsetLeft,
      //   y: this.refs.button.offsetTop
      // };

      var rect = elem.getBoundingClientRect();
      options = {
        x: rect.left,
        y: rect.top
      };

    };

    function removeTooltip(elem){
      options = null;
    };

        var buttonStyle = this.extend(outterStyleMy, iconStyleMy);

        return(
        <Style>
        {`
          .IconButton{
            position: relative;
          }
          .IconButton:disabled{
            color: ${_props.disabledColor};
          }
          .btnhref{
            text-decoration: none;
          }
        `}
         <a {...opts} className="btnhref" > 
          <Tooltip text={_props.tooltip} position={this.options} />
          <button ref="button" className={"IconButton" + _props.className} disabled={disabled} style={buttonStyle}
          onMouseEnter={showTooltip(this)} onMouseLeave={removeTooltip(this)} >
            <Ink background={true} style={rippleStyle} opacity={rippleOpacity} />
            <FontIcon className={_props.iconClassName}/>
          </button>
        </a>
        </Style>
        );

  }
});

ReactDOM.render(
 <IconButton href="" className="" iconStyle="" style="" iconClassName="face" disabled="" disableTouchRipple="" tooltip="aaaaa" />,
 document.getElementById('app')
);

And I have no idea what's wrong :/ Thanks for help :)



Solution 1:[1]

functions showTooltip(elem) and removeTooltip(elem)

should be outside render() method and bound to IconButton context. Otherwise, this.refs will be undefined.

<button ref="button" 
className={"IconButton" + _props.className} 
disabled={disabled} 
style={buttonStyle}
onMouseEnter={this.showTooltip.bind(this)} 
onMouseLeave={this.removeTooltip.bind(this)} >

alse, i think showTooltip and removeTooltip functions are wrong. They should takes the event as argument, eg::

function showTooltip(event){
  var elem = event.target
  var rect = elem.getBoundingClientRect();
  options = {
    x: rect.left,
    y: rect.top
  };

};

But now I'm getting this error: "Uncaught TypeError: Cannot read property 'y' of undefined". Meybe there is something wrong with process of passing informations?

You get this error becasuse of this construction:

render(){
   var _props = this.props,
         style = {
            top: options.y,
            left: options.x,
         };

         return(
            <div className="tooltip" style={style}>{_props.text}</div>
         );
  }

Even if you declare something like var options you still have no guarantee that options will be defined and accessible in Tooltip render method.

In general - your code is bad. I strongly suggest you to take some react tutorials, learn about state, props and react component lifecycle. ES6 knowledge won't hurt as well.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1