A
A
alvvi2017-07-30 15:13:52
JavaScript
alvvi, 2017-07-30 15:13:52

What is the correct way to get the clientWidth of a DOM element after rendering?

There is a navigation component, at the moment it looks like this:

spoiler
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import styles from './Nav.styl';

export default class Nav extends Component {    
    state = {
        boundryOffset: 0,
        boundryWidth: 0
    }

    updateBoundry = (target) => {
        if (target && target.firstChild.classList.contains('active')) {
            this.setState((prevState) => ({
                ...prevState, 
                boundryOffset: target.firstChild.offsetLeft,
                boundryWidth: target.firstChild.clientWidth, 
            }))
        }
    }

    render() {
        return (
            <nav ref="nav" className={styles.nav}>
                <span 
                    style={{
                        transform: `translateX(${this.state.boundryOffset}px)`,
                        width: `${this.state.boundryWidth}px`
                    }}
                    className={styles.boundry}></span>
                <li className={styles.item} 
                    ref={this.updateBoundry}>
                    <NavLink exact className={styles.link} to='/'>Home</NavLink>
                </li>
                <li className={styles.item} 
                    ref={this.updateBoundry}>
                    <NavLink  exact className={styles.link} to='/training'>Decks</NavLink>
                </li>
            </nav>
        );
    }
}

In short: I'm trying to make a floating element that will be under the active link.
As you can see, when calling ref-callback, I check if li contains an active link and try to get some properties from this link, and then I call setState to cause rendering and moving the boundry element accordingly, but the problem is that it clientWidthreturns 0, but it’s offsetLeftnot clear where did 32 come from. I get the same results when I try to select these elements with normal selectors in the componentDidMounthook, in other hooks and the console, the elements return the correct values.
here, when updating a component, everything will be calculated correctly, but not when mounting (
import React, { Component } from 'react';
import { NavLink } from 'react-router-dom';
import styles from './Nav.styl';

import ReactDOM from 'react-dom';

export default class Nav extends Component {
    constructor(props) {
        super();
    }
    
    componentDidUpdate() {
        this.updateBoundry();
    }

    componentDidMount() {
        this.updateBoundry();
    }

    updateBoundry = () => {
        const target = ReactDOM.findDOMNode(this).querySelector('.active');
        const boundry =  ReactDOM.findDOMNode(this).querySelector('.'+styles.boundry);

        
        if (target) {

            boundry.style.transform = `translateX(${target.offsetLeft}px)`
            boundry.style.width =  `${target.offsetWidth}px`

        }
    }

    render() {
        return (
            <nav ref="nav" className={styles.nav}>
                <span className={styles.boundry}></span>
                <li className={styles.item} >
                    <NavLink exact className={styles.link} to='/'>Home</NavLink>
                </li>
                <li className={styles.item}>
                    <NavLink  exact className={styles.link} to='/training'>Decks</NavLink>
                </li>
            </nav>
        );
    }
}

I use stylus-loader, css-modules, postcss if it matters.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
P
Pavel Kornilov, 2017-07-30
@KorniloFF

element.getBoundingClientRect() - everything you need is there.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question