July 20

Periodic Table with React

Hi again! So when I was working on my calculator I realized I needed more practice with CSS, especially grids. While researching I came across this blog post, where they make a Periodic Table with React. I felt inspired to do the same. It would be a nice way to practice on my CSS skills! Apologies for the short blog post, since I’m on a little time crunch here!

I planned my app to work like this: It would show all of the elements like the Periodic Table, and if you hover over one them it will show an info screen at the bottom. The data would be imported through a JSON file which was created by “Bowserinator” and can be downloaded from his GitHub page. This is how to final product looks like:

Making the React components themselves was an easy task. I first created an “Element” component that got it’s element number as a prop, and then loads the corresponding element data from the JSON file.


export class Element extends Component {

    constructor(props) {
        super(props)

        //Make sure it's not the '57-71' or '89-103'
        if (!props.notElement) {

            //Get the right element data from the JSON file
            let element = dataFile.elements.filter(e =>
                e.number === props.nr
            );

            //Set the data
            this.state = {
                element: element[0]
            }
        }
    }


    render() {
        if (!this.props.notElement) {
            // Show element
            const { element } = this.state;

            return (
                <div className={`component-element element-${element.number} ${element.category}`} onMouseOver={() => this.props.handler(element)}>
                    <div className="number">{element.number}</div>
                    <div className="symbol">{element.symbol}</div>
                </div>
            )
        } else {
            // Show '57-71' or '89-103'
            return (
                <div className={`component-element short${this.props.nr}`}>
                    <div className="number">{this.props.nr}</div>
                </div>
            )
        }
    }
}

To make the periodic table layout I created a CSS class like this:


  .table-elements {
    display: grid;
    grid-template-columns: repeat(17, auto) 1fr;
  }

First I need to set the display to be grid, and then I create a grid with 18 columns.

Since I’m going to have my Periodic Table with the middle elements by itself under (like most periodic tables do), I had to create an additional grid with 14 rows:


  .table-elements-extra {
    display: grid;
    grid-template-columns: repeat(14, auto) 1fr;

    padding-top: 5%;

    margin-left: auto;
    margin-right: auto;

    width: 80%;
  }

Using margin-left and margin-right I can center the additional grid.

To make sure the elements get positioned correctly I made some CSS classes that set the grid’s column starting point:


.element-2 {
    grid-column-start: 18;
}

.element-5, .element-13 {
    grid-column-start: 13;
}

The number corresponds to the element number. Since I have:


<div className={component-element element-${element.number} ${element.category}}

in my Element component, it automatically applies the correct CSS class to the elements with the number. I also use category for setting the elements background color.

In my App component I create several list’s with just numbers in them:


  constructor(props) {
    super(props)

    this.createElements();

    this.state = {
      selectedElement: null
    }
  }

  createElements() {
    this.elements_1To56 = [];
    this.elements_57To71 = [];
    this.elements_72To88 = [];
    this.elements_89To103 = [];
    this.elements_104To118 = [];

    for (let i = 1; i <= 56; i++) this.elements_1To56.push(i);
    for (let i = 57; i <= 71; i++) this.elements_57To71.push(i);
    for (let i = 72; i <= 88; i++) this.elements_72To88.push(i);
    for (let i = 89; i <= 103; i++) this.elements_89To103.push(i);
    for (let i = 104; i <= 118; i++) this.elements_104To118.push(i);
  }

The reason I do several lists is because I have to divide the table into multiple parts. This makes it easier to create the table.

To render them I have to use the map function to iterate through the lists:


render() {

    return (
      <div className="wrapper">

        <h1>Periodic Table</h1>

        <div className="table-elements">

          {/* Elements 1 - 56 */}
          {this.elements_1To56.map((element, index) => {
            return <Element handler={this.setActiveElement} key={element} nr={element} />
          })}

          <Element nr="57-71" notElement />

          {/* Elements 57 - 71 */}
          {this.elements_72To88.map((element, index) => {
            return <Element handler={this.setActiveElement} key={element} nr={element} />
          })}

          <Element nr="89-103" notElement />

          {/* Elements 104 - 118 */}
          {this.elements_104To118.map((element, index) => {
            return <Element handler={this.setActiveElement} key={element} nr={element} />
          })}

        </div>

        <div className="table-elements-extra" >
          {/* Elements 57 - 71 */}
          {this.elements_57To71.map((element, index) => {
            return <Element handler={this.setActiveElement} key={element} nr={element} />
          })}

          {/* Elements 89 - 103 */}
          {this.elements_89To103.map((element, index) => {
            return <Element handler={this.setActiveElement} key={element} nr={element} />
          })}
        </div>

It looks a little ugly, but it’s a lot better than having 118 lines for each element.

Last, I want to have a Info bar at the bottom that shows info about the element that’s currently being hovered over. The component takes an Element object, that has the data from the JSON file, as a prop.


export class InfoBar extends Component {
    render() {
        if (this.props.element !== null) {
            const { element } = this.props;

            return (
                <div className="info-bar">

                    <div className="element-short-info-container">
                        {/* Show element box */}
                        <div className={`component-element big ${element.category}`}>
                            <div className="number-big">{element.number}</div>
                            <div className="symbol-big">{element.symbol}</div>
                        </div>

                        <div className="element-mass">Mass: {element.atomic_mass}u</div>
                        <div className="element-electron">Category: {element.category}</div>
                    </div>

                    {/* Element name */}
                    <div className="element-info-container">
                        <div className="element-name">{element.name}</div>
                        <div className="element-desc">{element.summary}</div>
                    </div>
                </div>
            )
        } else {
            return <div> </div>
        }
    }
}

The last thing to do is add a hover handler for our elements in our App component that I will pass down to the elements as a prop:


  setActiveElement = element => {
    this.setState({
      selectedElement: element
    })
  }

And it’s done!

I’m really happy about how the project ended up. I got to practice the things I needed and learned a lot more about CSS and styling. I can already feel that I’m getting a lot more confident with React as well, since the project only took so little time to make. Most of it went into learning CSS.

Here’s the link to the GitHub page! I’m also gonna research how I can upload live demos from my demos!

Thanks for reading!



Copyright 2020. All rights reserved.

Posted July 20, 2020 by Atle in category "ReactJS

Leave a Reply

Your email address will not be published. Required fields are marked *