【HTML5】Grid 布局实现元素周期表的展示

论坛 期权论坛 期权     
自在遨游   2019-6-10 03:55   4397   0
先看看最终的效果图


以前和同事闲话吹牛说还能背诵元素周期表前20个元素,后来默想了一下已经背不起来了,又一次日常打脸
。后来偶尔想起来这个事儿,总想翻翻元素周期表(主要还是元素周期表太“漂亮”了,非常好的体现了元素的性质规律)。
这两天又想起这个事儿,就想这个东西用 HTML5 表现的话是不是很容易?看结构似乎用 CSS 的 Grid 布局方式比较好做,刚好也熟悉下 Grid 布局特性。

在网上搜了下元素周期表,总整体布局来看分为两大块,第一部分就是上面凹字形的元素表了。第二部分就是镧系元素和锕系元素的扩展展示部分。
那么 HTML 的部分就先放两个 Div 充当这两个部分的容器。
  1. [/code][code]
复制代码
  1.    
复制代码
  1.    
复制代码
  1.     元素周期表
复制代码
  1. [/code][code]
复制代码
  1.    
复制代码
  1.    
复制代码
  1. [/code][code]
复制代码
上面代码中的两个 div 就是容器了。他们的 id 分别设置为 periodicTable 和 series(变量名字不要太在意,我词汇匮乏每次起名都很费劲
),都有一个 container 类名。

下面再看每个化学元素的展示,如下图所示:


每个元素包含三个部分,左上角的原子量,右上角的元素符号,下面的元素中文名称。很容易写出氢元素的 HTML 结构如下:
  1. [/code][code]  1
复制代码
  1.   H
复制代码
  1.   氢
复制代码
  1. [/code]外层用一个 div 包裹,里面元素的具体内容用三个 span 表示。按代码所示分别给各个 HTML 元素添加一个类名。将上面这段代码放在 id 为 periodicTable 的 div 内部。
  2. HTML代码如下所示:
  3. [list][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][*][/list][code]
复制代码
  1. [/code][code]
复制代码
  1.    
复制代码
  1.    
复制代码
  1.     元素周期表
复制代码
  1. [/code][code]
复制代码
  1.    
复制代码
  1.         
复制代码
  1.             1
复制代码
  1.             H
复制代码
  1.             氢
复制代码
  1.         
复制代码
  1.    
复制代码
  1.    
复制代码
  1. [/code][code]
复制代码
页面效果如下:


元素的原子数,元素符号,中文名称都在一行,怎么把它们按照元素周期表上的那种方式排列呢?方法有很多,这里我们采用 CSS 的 flex 布局实现。在 HTML 代码中的 Style 标签内添加如下 CSS 样式代码:
  1. .container {
复制代码
  1.   display: grid;
复制代码
  1.   /*
复制代码
  1.    * grid-template-columns 属性指定 Grid 中的 列 (主要是列宽)如何设置。
复制代码
  1.    * repeat 函数表明重复 18 次,设置 列宽 为 1fr,
复制代码
  1.    * fr 表示按比例设置列宽 18 个 1fr 就表示设置为 18 列,每列宽度是 1/(18*1)
复制代码
  1.    * 也就是 18 列等宽
复制代码
  1.    */
复制代码
  1.   grid-template-columns: repeat(18, 1fr);
复制代码
  1. }
复制代码
  1. .element {
复制代码
  1.   display: flex;
复制代码
  1.   padding: 8px;
复制代码
  1.   flex-wrap: wrap; /* 设置为需要换行 */
复制代码
  1.   border: 0.5px solid #666; /* 给元素添加一个灰色的边框 */
复制代码
  1. }
复制代码
  1. .element .atom {
复制代码
  1.   display: block;
复制代码
  1.   text-align: left;
复制代码
  1.   width: 50%;
复制代码
  1. }
复制代码
  1. .element .symbol {
复制代码
  1.   display: block;
复制代码
  1.   text-align: right;
复制代码
  1.   width: 50%;
复制代码
  1. }
复制代码
  1. .element .name {
复制代码
  1.   display: block;
复制代码
  1.   text-align: center;
复制代码
  1.   width: 100%;
复制代码
  1. }
复制代码
将 atom 和 symbol 的 宽度设置为 50%,并且设置他们的父元素(element 类)的 flex-wrap (换行模式)为 wrap(换行),这样 元素的原子量和元素符号就会占一行,元素名称就会落到第二行,这样就达到了我们想要的效果。
效果图:


我们再添加第二个元素--氦元素,看看效果。


可以看到氦元素和氢元素紧紧的挨在一起了,氦应该是在第一行最右边也就是第 18 列的位置。我们给氦元素的 div 添加一个 Grid 布局相关的属性来实现这个目的。
  1. [/code][code]    2
复制代码
  1.     He
复制代码
  1.     氦
复制代码
  1. [/code]代码
  2. [list][*][/list][code]style="grid-column-start: 18;"
复制代码
表示添加内联样式,设置  grid-column-start: 18; 表示这个本元素在 Grid 中的列位置从第18列开始(位置编号从 1 开始)。

现在 HTML 页面看起来是这样的:


可以看到氦元素已经到最右边去了。

如果这样一个个添加的话,也太麻烦了。每个元素的样式布局和 HTML 代码一样,对于这样重复的工作当然是使用代码实现最简单咯。

我们先用一个数组来表示所有元素。
  1. let elements = [
复制代码
  1.     { symbol: "H", name: "氢" },
复制代码
  1.     { symbol: "He", name: "氦" },
复制代码
  1.     { symbol: "Li", name: "锂" },
复制代码
  1.     /* 代码太长,简略表示一下 */
复制代码
  1. ];
复制代码
通过遍历数组 elements 来生成所有表示元素的 HTML 代码。然后将容器 HTML 元素的 innerHTML 设置为生成的代码。
具体 js 代码如下:
  1. let html = '';
复制代码
  1.     elements.forEach((item, i) => {
复制代码
  1.         html += `
复制代码
  1.   ${i+1}
复制代码
  1.   ${item.symbol}
复制代码
  1.   ${item.name}
复制代码
  1. `;
复制代码
  1.     });
复制代码
  1.     document.getElementById('periodicTable').innerHTML = html;
复制代码
页面效果如下:


还有几个地方需要调整。
  • 部分元素的位置需要调整,例如氦元素需要偏移到 18 列,硼元素需要偏移到 13 列。
  • 注意到元素表中有两个位置比较特殊,57号元素镧元素的位置实际上是多个元素,镧下面的锕元素的位置也是。现在的表中57号以后的元素原子数显示已经不对了,需要处理一下
  • 一般元素表会区分金属元素与非金属元素。
还需要提一下的是,在使用数组 elements 表示化学元素的时候,元素对象中并没有元素的原子数信息,在数组索引隐含了这个信息,这个做法不是太好,在每个对象中包含化学元素的原子信息是一个更好的做法(这里因为我是从网上查的元素信息,使用正则替换的方法生成的这个数组代码,缺失了原子数信息,这里偷懒就没补了)。

下面逐个解决上面的问题。首先是元素位置的调整,可以在遍历 elements 数组是根据化学元素的原子数进行判断,增加偏移信息设置。只有氦、硼、铝三个元素是需要偏移的,所以直接判断还可以接受。
数组遍历部分代码修改如下:
  1. elements.forEach((item, i) => {
复制代码
  1.   let atom = i + 1;
复制代码
  1.   let style = '';
复制代码
  1.   if (atom === 2) {
复制代码
  1.       style = 'style="grid-column-start:18;"'
复制代码
  1.   }
复制代码
  1.   if (atom === 5 || atom === 13) {
复制代码
  1.       style = 'style="grid-column-start:13;"'
复制代码
  1.   }
复制代码
  1.   html += `
复制代码
  1. ${atom}
复制代码
  1. ${item.symbol}
复制代码
  1. ${item.name}
复制代码
  1. `;
复制代码
  1. });
复制代码
接下来是第二个问题,对镧系元素及其后面元素的原子数的处理。由于 57 和 89 号元素的位置原子数是一个范围,元素名称也比较普通的长,所以两个位置的字体要缩小一下,以方便展示。
对于第三个问题,通过给金属元素及非金属元素设置不同的背景色来区分。

在样式部分增加如下样式代码:
  1. .element.nonmetal {
复制代码
  1.   background-color: #acc0f0;
复制代码
  1. }
复制代码
  1. .element.metal {
复制代码
  1.   background-color: #c89f4d;
复制代码
  1. }
复制代码
  1. .element.series {
复制代码
  1.   padding: 0;
复制代码
  1. }
复制代码
  1. .element.series .atom,
复制代码
  1. .element.series .symbol {
复制代码
  1.   font-size: 0.5em;
复制代码
  1. }
复制代码
  1. .element.series>span {
复制代码
  1.   color: #cf1c1c;
复制代码
  1.   width: 100%;
复制代码
  1.   text-align: center;
复制代码
  1. }
复制代码
在数组遍历函数中修改代码,根据化学元素的原子数进行判断。
  1. // 非金属元素的原子数范围列表
复制代码
  1.     const nonmetal = [
复制代码
  1.         [1, 2],
复制代码
  1.         [5, 10],
复制代码
  1.         [14, 18],
复制代码
  1.         [33, 36],
复制代码
  1.         [52, 54],
复制代码
  1.         [85, 86]
复制代码
  1.     ];
复制代码
  1.     // 根据原子数判断是否是金属元素
复制代码
  1.     const isNonmetal = (atom) => {
复制代码
  1.         return nonmetal.some(range => {
复制代码
  1.             return range[0] = atom;
复制代码
  1.         });
复制代码
  1.     }
复制代码
  1.     let html = '';
复制代码
  1.     elements.forEach((item, i) => {
复制代码
  1.         let atom = i + 1;
复制代码
  1.         let style = '';
复制代码
  1.         if (atom === 57) {
复制代码
  1.             atom = '57 - 71';
复制代码
  1.         } else if (atom > 57 && atom < 75) {
复制代码
  1.             atom = atom + 14;
复制代码
  1.         } else if (atom === 75) {
复制代码
  1.             atom = '89 - 103';
复制代码
  1.         } else if (atom > 75) {
复制代码
  1.             atom = atom + 14 + 14;
复制代码
  1.         }
复制代码
  1.         let className = 'metal';
复制代码
  1.         if (typeof atom === 'number') {
复制代码
  1.             className = isNonmetal(atom) ? 'nonmetal' : 'metal';
复制代码
  1.         } else {
复制代码
  1.             className += ' series';
复制代码
  1.         }
复制代码
  1.         if (atom === 2) {
复制代码
  1.             style = 'style="grid-column-start:18;"'
复制代码
  1.         }
复制代码
  1.         if (atom === 5 || atom === 13) {
复制代码
  1.             style = 'style="grid-column-start:13;"'
复制代码
  1.         }
复制代码
  1.         html += `
复制代码
  1.   ${atom}
复制代码
  1.   ${item.symbol}
复制代码
  1.   ${item.name}
复制代码
  1. `;
复制代码
  1.     });
复制代码
  1.     document.getElementById('periodicTable').innerHTML = html;
复制代码
现在页面看起来是这个样子的:


已经像模像样了,还差最后一点,对镧系元素和锕系元素的详细展示。有了上面的基础,这个就简单了。
样式部分添加代码如下:
  1. #series {
复制代码
  1.   margin-top: 32px;
复制代码
  1. }
复制代码
  1. .lanthanoids {
复制代码
  1.   background-color: #f4c2a7;
复制代码
  1. }
复制代码
  1. .actinoids {
复制代码
  1.   background-color: #e78b5b;
复制代码
  1. }
复制代码
js代码部分添加如下代码:
  1. const LaLu = [
复制代码
  1.       { symbol: "La", name: "镧" },
复制代码
  1.       { symbol: "Ce", name: "铈" },
复制代码
  1.       { symbol: "Pr", name: "镨" },
复制代码
  1.       { symbol: "Nd", name: "钕" },
复制代码
  1.       { symbol: "Pm", name: "钷" },
复制代码
  1.       { symbol: "Sm", name: "钐" },
复制代码
  1.       { symbol: "Eu", name: "铕" },
复制代码
  1.       { symbol: "Gd", name: "钆" },
复制代码
  1.       { symbol: "Tb", name: "铽" },
复制代码
  1.       { symbol: "Dy", name: "镝" },
复制代码
  1.       { symbol: "Ho", name: "钬" },
复制代码
  1.       { symbol: "Er", name: "铒" },
复制代码
  1.       { symbol: "Tm", name: "铥" },
复制代码
  1.       { symbol: "Yb", name: "镱" },
复制代码
  1.       { symbol: "Lu", name: "镥" }
复制代码
  1.     ];
复制代码
  1.     const AcLr = [
复制代码
  1.       { symbol: "Ac", name: "锕" },
复制代码
  1.       { symbol: "Th", name: "钍" },
复制代码
  1.       { symbol: "Pa", name: "镤" },
复制代码
  1.       { symbol: "U", name: "铀" },
复制代码
  1.       { symbol: "Np", name: "镎" },
复制代码
  1.       { symbol: "Pu", name: "钚" },
复制代码
  1.       { symbol: "Am", name: "镅" },
复制代码
  1.       { symbol: "Cm", name: "锔" },
复制代码
  1.       { symbol: "Bk", name: "锫" },
复制代码
  1.       { symbol: "Cf", name: "锎" },
复制代码
  1.       { symbol: "Es", name: "锿" },
复制代码
  1.       { symbol: "Fm", name: "镄" },
复制代码
  1.       { symbol: "Md", name: "钔" },
复制代码
  1.       { symbol: "No", name: "锘" },
复制代码
  1.       { symbol: "Lr", name: "铹" }
复制代码
  1.     ];
复制代码
  1.     let series = document.getElementById('series');
复制代码
  1.     let seriesHTML = '';
复制代码
  1.     LaLu.forEach((item, i) => {
复制代码
  1.         let atom = i + 57;
复制代码
  1.         let style = '';
复制代码
  1.         if (i===0) {
复制代码
  1.             style = 'style="grid-column-start:4;"'
复制代码
  1.         }
复制代码
  1.         seriesHTML += `
复制代码
  1.   ${atom}
复制代码
  1.   ${item.symbol}
复制代码
  1.   ${item.name}
复制代码
  1. `;
复制代码
  1.     });
复制代码
  1.     AcLr.forEach((item, i) => {
复制代码
  1.         let atom = i + 89;
复制代码
  1.         let style = '';
复制代码
  1.         if (i===0) {
复制代码
  1.             style = 'style="grid-column-start:4;"'
复制代码
  1.         }
复制代码
  1.         seriesHTML += `
复制代码
  1.   ${atom}
复制代码
  1.   ${item.symbol}
复制代码
  1.   ${item.name}
复制代码
  1. `;
复制代码
  1.     });
复制代码
  1.     series.innerHTML = seriesHTML;
复制代码
现在元素周期表就完整了(最后两个元素的中文名称输入法还打不出来,意会一下)。

最后还有个地方需要调整一下。
当页面宽度不足时,元素的显示空间会被压缩,变得很难看。所以我们在 container 的样式中设置一个合适的宽度,这样当页面宽度不足时会产生滚动条,不会导致显示变形。
  1. .container {
复制代码
  1.   display: grid;
复制代码
  1.   grid-template-columns: repeat(18, 1fr);
复制代码
  1.   width: 1206px;
复制代码
  1. }
复制代码
将 container 的 width 设置为 1206px 是个不错的值,1206 是 18 的整数倍(每行有 18 个元素),这样会避免一些细微的像素级偏差问题。

到此元素周期表的 HTML5 展示就完成了,点击下方阅读原文可以查看最终效果及代码。

Grid 布局功能是一个非常强大的功能,这篇文章不是 Grid 布局功能的详细介绍,只是稍稍展示了下 Grid 布局的威力,要想学习更多 Grid 布局的内容可以查看相关的 MDN 文章。

btw:现在公众号的插入代码功能比原来好多了啊,一般情况下也够用了,不用在别处编辑再粘贴过来了
,好久没写了之前搞代码排版的那个网页都找不到了

Grid  MDN 参考链接:
https://developer.mozilla.org/zh-CN/docs/Glossary/Grid
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP