Periodic Table of Elements in Svelte — Part 2

Gregory King
JavaScript in Plain English
7 min readDec 3, 2020

--

In part 1, we were able to hook up our data and render the periodic table using Svelte. If you need a short refresher, you can find part 1 here (https://medium.com/javascript-in-plain-english/periodic-table-of-elements-in-svelte-52fafddb19dd).

Currently, our periodic table doesn’t do anything interesting, and honestly doesn’t add any extra value over a print version. Let’s change that by adding some interactivity to make it more useful. In the process, we also get to see how components communicate in Svelte and how easy it is to wire things up.

Project setup

If you are continuing from where we left off in Part 1, you can continue from there. If you somehow lost or can’t find the project, you can always clone the repository and install the dependencies as show below:

git clone https://github.com/kinxiel/Periodic-Table-of-Svelements.gitcd Periodic-Table-of-Svelements/
npm install
npm run dev

If there are no errors, head over to localhost:5000 to find your rendered periodic table.

What are we going add?

Currently, our periodic table only displays the name and symbol for each element, but I think we can do better than that. If we check the json containing the data at /src/Data/PeriodicTableJSON.json, we can see that there are a lot of other information available for each element. For example, for hydrogen we have information about its boiling point in a property called boil , its atomic mass in atomic_mass and etc.

Let’s find a way to get this information into our periodic table and also add some simple functionality to allow users to select which value they want displayed.

Adding additional information

Let’s start by adding the element’s atomic mass right below the element’s symbol. Open Element.svelte in /src/Components/Element.svelte and add the following lines of code in the <script> tag. Where you place this code doesn’t matter, but typically props you want to expose are placed near the top of the file. In my case, I added this right below export let atomicNumber and above the toCamelCase() function declaration.

export let selectedProperty = 'atomic_mass';$: additionalInformation = eval(
'elements[atomicNumber - 1].' + selectedProperty
);

In the first line, we expose a selectedProperty prop with a default value that other components can access. In the second line we have$:

This is known as a reactive statement in Svelte, however the syntax is not unique to Svelte. It is used in vanilla JavaScript, and is known as a labeled statement. It is used to break or continue statements in loops, but not used very often. Because Svelte is a compiler, it can give these types of syntax new meaning.

Reactive statements can be used to capture changes in variables it references. In the example above, the variable additionalInformation is updated anytime, a variable it references (e.g. selectedProperty) changes. Currently, selectedProperty is set to a static value so we can’t take advantage of it right now, but we will shortly.

It is also important to note, that when you assign a variable as a reactive statement, you do not need to declare it with let, Svelte does this for you under the hood (Thanks Svelte!).

Finally, eval is vanilla JavaScript used to evaluate a string as code. So the example above translates to element[0].atomic_mass for hydrogen.

For the markup, add the following code right below the div containing {elements[atomicNumber — 1].name}.

<div class="mx-auto overflow-hidden font-light text-center element-name">
{additionalInformation}
</div>

Save the file and if done correctly, you should now see the atomic mass for each element displayed below their symbol. If you run into problems, the gists and source code are available at the end of the article.

Creating the property menu

Now that we have a way to display additional information for each element, let’s allow users to view other properties as well.

Let’s start by creating a selection menu. Open Table.svelte in /src/Components/Table.svelte. Add the following inside the <script /> tag.

// Initial value for the select element
let selection = {
value: 'atomic_mass',
text: 'Atomic Mass',
};
// These are the options in the select element
let properties = [
{ value: 'atomic_mass', text: `Atomic Mass`},
{ value: 'boil', text: `Boiling Point` },
{ value: 'category', text: `Category` },
{ value: 'density', text: `Density` },
];

We want to put this select element inside the table, on top of Boron (B) and to the left of Helium (He). Add the following CSS inside the <style /> tag.

.menu {
grid-column: 13;
grid-row: 1;
grid-column-end: 17;
}
.menu select {
font-size: 1.5vw;
width: 20vw;
}

Finally add the following markup, right above the {#each /} block we used to loop over the elements in part 1.

<div class="menu">
<select
bind:value={selection}
class="border border-blue-500 shadow sm:mt-2 md:mt-4">
{#each properties as property}
<option value={property}>
<span>{property.text}</span>
</option>
{/each}
</select>
</div>

Basically, we loop through our list of properties to populate our select element with the appropriate options. The bind directive is used to link the value of the select element directly to a variable. In this case, we link the selection prop to our select element.

The behavior of bind:value varies depending on where it is used, but typically it is used to link values between input elements (e.g. input, checkbox, select) and variables. The bind directive is used heavily in Svelte and there are other uses for it aside from those mentioned above, but to keep things simple, let’s focus on this use case for now.

As a side note, the classes sm:mt-2 and md:mt-4 are TailwindCSS classes, which tell the browser to set the top margin to 0.5rem on small sized screens and 1rem on medium sized screens respectively.

Save the file and you should now see a select menu to the left of helium (He). You can select an option from the menu, but it won’t do anything yet, so let’s work on that.

Let’s make things reactive

At this point we have more or less everything setup, we just need to add the final connection to link our <Element /> component to our <Table /> component.

In Table.svelte, add the following right below the properties array, just before closing the <script /> tag.

<script>// ... properties array declaration ...$: selectionProperty = selection.value;</script>

We create a new reactive variable named selectionProperty and set its value to selection.value whenever the value of the select element changes.

Finally, edit the markup in the {#each /} block to look like the code below.

{#each elements as element, i}
atomicNumber={element.number}
// Add this line below
bind:selectedProperty={selectionProperty}
style="grid-column: {element.xpos}; grid-row: {element.ypos}" />{/else}{null}{/each}

Here we use the bind directive again, but in a slightly different way. This time, we are using it to link the child component’s props to the parent component’s props.

There are many different ways for components to communicate in Svelte. The example above is one such example. Personally, I think this is one of the easiest and most intuitive ways to do it. This can be summed up in the following pattern.

<childComponent bind:childProp = {parentProp} />

In our code, we are basically linking the value of {selectedProperty}, which is a prop exported by our <Element /> component, to the selectionProperty prop in our <Table /> component. We also know, that when a change occurs in selectedProperty, this also triggers the value of additionalInformation to update, which ultimately results in the value updating in the periodic table.

Run npm run dev in your terminal, if it’s not already running and head over to localhost:5000. You should now have a fully functioning menu, which allows users to change the supplementary information displayed in the table.

Adding some polish

With the previous step, we are basically done with our periodic table. However, you may have also noticed that some numbers are not formatted properly and it would also help to add some context by adding the some units, so let’s fix that.

For density we need to conditionally render the correct units, because density depends on the phase of the element. In Element.svelte, add the following just before the toCamelCase() function declaration near the end of the <script /> tag.

$: phaseInformation = eval('elements[atomicNumber - 1].' + 'phase');

We are basically getting the phase of the element (solid, liquid, gas) and storing that information in phaseInformation. We can then use this to display the appropriate units.

Next, replace {additionalInformation} near the end of the markup section to look like the code below.

{#if additionalInformation == null}
{:else if selectedProperty == 'atomic_mass'}
{additionalInformation.toPrecision(4)}
{:else if selectedProperty == 'density'}
{#if phaseInformation == 'Gas'}
{additionalInformation} g/L
{:else}
{additionalInformation} g/cm<sup>3</sup>
{/if}
{:else if selectedProperty == 'boil'}
{additionalInformation} K
{:else}
{additionalInformation}
{/if}

Lots of conditionals, but it basically renders the correct units depending on the selection. For the last time, run npm run dev in your terminal and play with the menu and see if you get the expected results, and we’re done!

If you got stuck during the walkthrough, you can find the complete code for the <Element /> and <Table /> components below.

Element.svelte

Table.svelte

Conclusion (The disappearing act)

In this post, we explored how easy and intuitive it is to perform data communication between components in Svelte. The framework provides a lot of handy tools that make it easy to implement common tasks, like binding prop values. Despite the super powers Svelte gives you, it still feels very manageable and I think one reason for this is that at the end of the day, Svelte code looks very much like HTML, CSS and JavaScript, which is familiar to most people and that is what is magical about it.

Svelte is also known as the “disappearing framework” primarily because after compilation, it allows you to ship very small code bundles, which is good for performance. I think another reason why Svelte “disappears”, is the intuitive and simple syntax. While working in Svelte, I often forget that I am actually using a framework and that allows me to focus on the content rather than on the tools.

Feel free to let me know if you have any comments or questions and you can check the complete source code on https://github.com/kinxiel/Periodic-Table-of-Svelements-Part2. Thanks for reading!

--

--