4

Situation: I want to obtain dimensions and position of an element

Context: I am making a card game. Cards can be placed on the board. I need to be able to check that their destination is on the board. I then will store their position relative to the board's dimensions and location. This will help to reproduce the board across a bunch of different clients who may be using different screen dimensions.

Current Styling:

The board's size width and height are defined by:

width: 100%;
flex-grow:1;

It's container has these properties:

display: flex;
flex-direction: column;

Image of Board

You should note that the actual dimensions are [942.922x874]. I cannot find the position information, I assume because default div position:static instead of position:absolute.

My Attempts:

let board = document.getElementById("gameboard");
if (board) {
  console.log("style.height", board.style.height);
  console.log("style.width", board.style.width);
  console.log("offsetHeight", board.offsetHeight);
  console.log("offsetWidth", board.offsetWidth);
  console.log("getBoundingClientRect" , board.getBoundingClientRect());
}

Output of my attempts

Note that the output dimensions are different than my actual dimensions. [1137, 920] instead of [942.922x874]. The [1137, 920] are actually the dimensions of the entire body. Also no matter what I do to obtain position it always gives me [0,0].

EDIT-1

We've come to the conclusion that this may be a timing issue. That I need to make sure that the code to retrieve the dimensions and position are run after the elements have been rendered. Since I am using react I found this thread, ReactJS - Get Height of an element. Which basically says I need to hook into React's component lifecycle method componentDidMount() which runs immediately after rendering has been completed. You can see my implementation below. However, even with this implementation I seem to still be getting the dimensions of body container instead of the element I am accessing.

import React, {PureComponent, PropTypes} from 'react';
// import Card from '../../Components/Card/index.js';
// import Collection from '../../Components/Collection/index.js';
import {List} from 'immutable';
import Indicator from '../../Components/Indicator/index.js';
import Counter from '../../Components/Counter/index.js';
import {connect} from 'react-redux';
import * as actionCreators from './../../action_creators.js';
import './styles.scss';

export default class Game extends PureComponent {
  componentDidMount() {
    let board = document.getElementById("gameboard");
    console.log("getBoundingClientRect" , board.getBoundingClientRect());
  }
  render() {
    let playersById = this.props.playersById;
    let collections = this.props.collections;
    let counters = this.props.counters;

    let indic_list = [];
    let coll_list = [];

    //Indicators
    playersById.forEach(function(value, key, map){
      indic_list.push(<p className="section-name">{playersById.get(key).get('name')+"'s Indicators"}</p>)
      playersById.get(key).get('indicators').forEach(function(value, key2, map){
          indic_list.push(<Indicator playerId={key} action={this.props.modIndicator} label={key2}>{value}</Indicator>)
      }, this);
    },this);

    //Collections
    collections.forEach(function(value, key, map) {
      coll_list.push(<span>{key}: {collections.get(key).get("content").size}</span>);
      collections.get(key).get("control").forEach(function(value, key, map){
          coll_list.push(<span>Control: {value}</span>);
      });
    });

    return (
        <div className="frame">
          <div className="left-col">
            {indic_list}
          </div>
          <div className="center-col">
            <span className="collections">
              {coll_list}
            </span>
            <div id="gameboard"></div>
          </div>
          <div className="right-col">
            <div className="counters">
              {counters.map(function(type){
                return <Counter label={type}></Counter>
              })}
            </div>
          </div>
        </div>
      )
  }
}
Game.PropTypes = {
  playersById:  PropTypes.object.isRequired,
  collections: PropTypes.object.isRequired,
  counters: PropTypes.object.isRequired,
  modIndicator: PropTypes.func.isRequired
}
function mapStateToProps(state) {
  return {
    playersById: state.get('playersById') || new List(),
    collections: state.get('collections') || new List(),
    counters: state.get('counters') || new List()
  };
}
export const GameContainer = connect(mapStateToProps, actionCreators)(Game);

Edit-2 Solution The solution to my problem came from this thread. Get the height of a Component in React

The following is the implementation in my code.

import React, {PureComponent, PropTypes} from 'react';
// import Card from '../../Components/Card/index.js';
// import Collection from '../../Components/Collection/index.js';
import {List} from 'immutable';
import Indicator from '../../Components/Indicator/index.js';
import Counter from '../../Components/Counter/index.js';
import {connect} from 'react-redux';
import * as actionCreators from './../../action_creators.js';
import './styles.scss';

export default class Game extends PureComponent {
  render() {
    let playersById = this.props.playersById;
    let collections = this.props.collections;
    let counters = this.props.counters;

    let indic_list = [];
    let coll_list = [];

    //Indicators
    playersById.forEach(function(value, key, map){
      indic_list.push(<p className="section-name">{playersById.get(key).get('name')+"'s Indicators"}</p>)
      playersById.get(key).get('indicators').forEach(function(value, key2, map){
          indic_list.push(<Indicator playerId={key} action={this.props.modIndicator} label={key2}>{value}</Indicator>)
      }, this);
    },this);

    //Collections
    collections.forEach(function(value, key, map) {
      coll_list.push(<span>{key}: {collections.get(key).get("content").size}</span>);
      collections.get(key).get("control").forEach(function(value, key, map){
          coll_list.push(<span>Control: {value}</span>);
      });
    });

    return (
        <div className="frame">
          <div className="left-col">
            {indic_list}
          </div>
          <div className="center-col">
            <span className="collections">
              {coll_list}
            </span>
            <div id="gameboard" ref={(node) => this.calcHeight(node)}></div>
          </div>
          <div className="right-col">
            <div className="counters">
              {counters.map(function(type){
                return <Counter label={type}></Counter>
              })}
            </div>
          </div>
        </div>
      )
  }
  calcHeight(node){
    if (node) {
        console.log("calcHeight", node.getBoundingClientRect());
      }
  }
}
Game.PropTypes = {
  playersById:  PropTypes.object.isRequired,
  collections: PropTypes.object.isRequired,
  counters: PropTypes.object.isRequired,
  modIndicator: PropTypes.func.isRequired
}
function mapStateToProps(state) {
  return {
    playersById: state.get('playersById') || new List(),
    collections: state.get('collections') || new List(),
    counters: state.get('counters') || new List()
  };
}
export const GameContainer = connect(mapStateToProps, actionCreators)(Game);
Community
  • 1
  • 1
Alex Donley
  • 145
  • 3
  • 16
  • if you want your board to become an offsetParent (so that the position of elements inside it are relative to it and not the body), you'll have to give it a `position: relative`, for example – Touffy Apr 06 '17 at 19:28
  • What if you run that code live in the console after the page has loaded - do you get different results? – MDEV Apr 06 '17 at 19:28
  • If I run it live I get the correct output. getBoundingClientRect ClientRect {top: 46, right: 1058.515625, bottom: 920, left: 115.59375, width: 942.921875…} – Alex Donley Apr 06 '17 at 19:32
  • Probably a timing issue then, it needs to move down the code or run from a timeout/event handler – MDEV Apr 06 '17 at 19:34
  • Yah that is probably the issue. I am using react to render the UI. Your answer led me to find this thread, which I think may be able to solve my problem. http://stackoverflow.com/questions/35153599/reactjs-get-height-of-an-element They say that it's taking the dimensions from the container. I need to get the dimensions after it renders by hooking up into componentDidMount. I will test it out and post the results here. Thank you. – Alex Donley Apr 06 '17 at 19:42
  • Make sure you run your script _after_ the body been loaded – Asons Apr 06 '17 at 19:46
  • have you tried at `getBoundingClientRect`? that returns width and height – John Ruddell Apr 06 '17 at 20:20
  • John, yes I tried that. That function was used in the "My Attempts" section. – Alex Donley Apr 06 '17 at 23:52
  • Mike I just saw your comment. I actually did end up using that example for my answer. So my question could be considered duplicate. – Alex Donley Apr 07 '17 at 16:16
  • Can you please add answers as actual responses instead of mixing them into the question? It's very confusing. – Micros Oct 20 '17 at 11:42

2 Answers2

1

As I stated in my final edit, the solution for this problem came from this thread. Get the height of a Component in React The answer was influenced by the comments from SmokeyPHP where I was trying to access the properties before the elements were actually rendered. I needed to make sure to access them afterwards to get accurate information.

Community
  • 1
  • 1
Alex Donley
  • 145
  • 3
  • 16
-1

You should use the .offsetWidth and .offsetHeight properties.

Note they belong to the element, not .style.

var width = document.getElementById('foo').offsetWidth;
Quentame
  • 254
  • 1
  • 4
  • 13
JFrez
  • 39
  • 1
  • 4
  • OP is already using those....? And if you read here, you'll see its more to it than just that: https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/Determining_the_dimensions_of_elements – Asons Apr 06 '17 at 19:47
  • Since this answer does add anything, I recommend you either update it or delete it, or else you most likely will be down voted – Asons Apr 06 '17 at 19:50