Web制作の技術は日々進化しており、会社やプロジェクトによっては昨今の環境に適さない書き方をしているケースも時折見受けられます。
そこで今回は「2024年のWeb制作ではこのようにコードを書いてほしい!」という内容をまとめました。
質より量で、まずは「こんな書き方があるんだ」をこの記事で伝えたかったので、コードの詳細はあまり解説していません。なので、具体的な仕様などを確認したい方は参考記事を読んだりご自身で調べていただけると幸いです。!
当記事では「iOS Safari バージョン15系以上」でサポートされている技術を基本的に紹介しています。しかし、15系や16系でサポートされていない技術も少しだけ含まれているので、その場合は補足をしておりますのでご留意ください。
1. HTML
- Lazy loading
- Picture要素
- Details要素
- Dialog要素
- Hgroup要素
- Dl要素
- Grid Layout
- Subgrid
- gap
- :has / :is / :where
- object-fit
- aspect-ratio
- inset
- margin-inline: auto
- place-content: center
- width: fit-content
- word-break
- transform
- transition
- filter
- clip-path
- currentColor
- clamp()
- svh
- border-radius: 100vmax
- @media (min-width: 768px) & range記法
- any-hover: hover
- prefers-reduced-motion: reduce
- Visually Hidden
- 親要素の左右にpaddingが指定されている状態で子要素の横幅を画面幅と同じにするレイアウト手法
- コンテンツ幅から片方だけ画面の端まではみ出しているレイアウト手法
- メインコンテンツが少ない状態でもフッターを画面最下部に固定させるレイアウト手法
- Defer
- DOMContentLoaded
- Debounce
- Intersection Observer
- matchMedia
- 375px未満のレスポンシブ対応
- ES6以降の記法
- 文字列の結合
- 配列操作
- スプレッド構文
- Async / await
- Fetch / Axios
Lazy loading
<img>
にloading="lazy"
属性を付けると画像が遅延読み込みになり、サイトの読み込み時間が早くなります。
<img src="..." alt="" width="600" height="400" loading="lazy">
loading="lazy"
属性の補足です。
width
とheight
が必須(レイアウトシフト対策にもなるので必ず付けましょう)iframe
要素にも使える- 仲間的な
decoding="async"
はあまり意味がない
Picture要素
画面幅に応じて画像を出し分ける時は<picture>
を使います。
CSS側(display: none
など)で画像を出し分けると、小さい画面幅の時には不要な「大きい画面幅用の画像」も読み込まれるのでサイトパフォーマンスが悪くなります。
<picture>
<source media="(min-width:768px)" srcset="lerge.png" width="400" height="200">
<img src="small.png" alt="" width="80" height="40">
</picture>
Details要素
アコーディオンの実装には<details>
を使います。ページ内検索で閉じているアコーディオンの中身もヒットしたり、開閉処理が備え付けられているなどのメリットがあります。
<details>
<summary>タイトル</summary>
アコーディオンの中身
</details>
開閉処理のアニメーションには、GSAPやgrid-template-rows
を使った方法があります。
Dialog要素
モーダルウィンドウの実装には<dialog>
を使います。アクセシビリティに優れていたり、z-index
を使わなくても最上位に表示されるなどのメリットがあります。
<dialog open>
<div>モーダルのコンテンツ</div>
<form method="dialog">
<button>閉じる</button>
</form>
</dialog>
iOS Safariのバージョン15.3以下がサポート外なので、プロジェクトの要件に満たしているかを必ず確認しましょう。
Hgroup要素
見出しに複数の要素(主題+副題)がある場合は<hgroup>
でグルーピングします。
<hgroup>
<h2>DX支援事業</h2>
<p>経営課題をDXで解決</p>
</hgroup>
Dl要素
<dl>
の直下には<div>
を置き、その直下に<dt>
と<dd>
を置くことでスタイリングがしやすくなります。最新のWHATWGの仕様では<dl>
の直下に<div>
を置けるようになっています(<div>
を置かなくても仕様的には問題ありません)。
🥳 Good!!
<dl>
<div>
<dt>クラウドコンピューティング</dt>
<dd>インターネット経由でコンピューターの資源を提供する...</dd>
</div>
<div>
<dt>API</dt>
<dd>Application Programming Interfaceの略で、ソフトウェア間で...</dd>
</div>
</dl>
🤮 Bad…
<dl>
<dt>クラウドコンピューティング</dt>
<dd>インターネット経由でコンピューターの資源を提供する...</dd>
<dt>API</dt>
<dd>Application Programming Interfaceの略で、ソフトウェア間で...</dd>
</dl>
2. CSS
モダンなCSSを取り入れるなら、まずはレイアウト手法のGrid Layoutに慣れることからスタートするといいでしょう。また、CSSは特にブラウザのサポート状況が複雑なので、Can I use…などでしっかりと確認してから実務に取り入れてください。
Grid Layout
記事一覧などの格子状のレイアウトはGrid Layoutで実装します。Flexboxに比べ、レスポンシブ時に要素の順番や大きさが変わるケースにも対応ができたり、calc()
を使った横幅や余白の複雑な計算も不要になります。
例1
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3列に並べる */
gap: 40px; /* 子要素の上下左右の間隔 */
}
例2
.grid {
display: grid;
grid-template-areas: "thumb title" "thumb description"; /* 付けた名前を並べる */
grid-template-columns: 300px 1fr; /* 1列目は300px、2列目は余った幅全て */
}
/* gridの子要素 */
.grid_title {
grid-area: title; /* 名前を付ける */
}
.grid_description {
grid-area: description;
}
.grid_thumb {
grid-area: thumb;
}
Subgrid
Grid Layoutで並べた各アイテム内の要素の縦位置を揃えたい時にSubgridを使います。こちらの例では、説明文の高さがバラバラでも日付の縦位置が同じ位置になるように実装しています。
.grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 40px;
}
.card {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
gap: 20px;
}
iOS Safariのバージョン15.8以下がサポート外なので、プロジェクトの要件に満たしているかを必ず確認しましょう。
gap
Flexboxで横並びにした要素の余白を調整するならgap
プロパティを使います。margin
を使うと、calc
や◯◯-of-type
などの記述が発生するので複雑になってしまいます。
🥳 Good!!
.flex {
display: flex;
gap: 20px;
}
🤮 Bad…
.flex {
display: flex;
}
.child {
margin-left: 20px;
}
.child:first-of-type {
margin-left: 0;
}
:has / :is / :where
便利な擬似クラスがここ数年で追加されました。
擬似クラス | 解説 | 備考 |
---|---|---|
:has() | 引数に指定した子孫要素を持つ場合、自分自身にマッチ | iOS Safari バージョン15.3以下ではサポート外 |
:is() | 引数に指定した要素にマッチ | 詳細度は通常計算 |
:where() | 引数に指定した要素にマッチ | 詳細度が常に0になる |
:has()
を使うことで、CSSだけで子要素の有無に応じてスタイルを変えられます(これまではJSを使っていました)。
:has()
/* .card の中に a が含まれているなら背景を赤に、含まれていないなら青にする */
.card {
background-color: blue;
}
.card:has(a) {
background-color: red;
}
:is()
:where()
を使うことで、親要素や前方隣接要素の状態に応じた記述が楽になります。
🥳 Good!!
.post:is(h2, h3, h4, h5, h6) {
font-weight: bold;
}
🤮 Bad…
.post h2, .post h3, .post h4, .post h5, .post h6 {
font-weight: bold;
}
Sassでも便利な使い方があります。以下はラジオボタンの選択状態に応じて背景色を変える例で、:is()
を使うことでspan
のブロック内にスタイルをまとめています。
🥳 Good!!
.radio {
span {
background-color: blue; // 未選択の時
&:is(input:checked + span) {
background-color: red; // 選択済みの時
}
}
}
🤮 Bad…
.radio {
span {
background-color: blue; // 未選択の時
}
input:checked + span {
background-color: red; // 選択済みの時
}
}
object-fit
background-size
プロパティの挙動を使うために画像をbackground-image
プロパティで読み込むのは古い手法です。昨今では<img>
で読み込んだ画像に対してobject-fit
を使うことで、background-size
と全く同じ挙動を再現できます。
<img>
を使えば遅延読み込みなどの恩恵を受けられるので、画像はできるだけ<img>
で読み込むようにしましょう。
🥳 Good!!
.img {
width: 100px;
height: 100px;
object-fit: cover;
}
🤮 Bad…
.img {
width: 100px;
height: 100px;
background-image: url(...);
background-size: cover;
}
aspect-ratio
画像の比率を制御するにはaspect-ratio
プロパティを使います。padding-top
を%
で指定する昔ながらの手法もありますが、aspect-ratio
のほうが記述が簡潔で分かりやすいです。
🥳 Good!!
.img {
width: 100px;
height: 100px;
aspect-ratio: 16/9; /* 縦横比を16:9に */
object-fit: cover; /* coverを指定しないと画像の縦横比が崩れる */
object-fit-position: center top; /* 必要に応じて画像の位置を調整 */
}
🤮 Bad…
.parent {
position: relative;
padding-top: 56.25%; /* 56.25% = 16:9 (9/16*100%) */
}
.child {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
inset
親要素全体に自身のサイズを広げる場合、inset
プロパティを使うと記述が簡潔になります。inset
はtop
left
right
bottom
を一括指定するショートハンドプロパティです。
🥳 Good!!
.element {
position: absolute;
inset: 0;
margin: auto;
}
🤮 Bad…
.element {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
margin-inline: auto
横幅を指定している要素の左右中央寄せはmargin-inline: auto
を使います。
🥳 Good!!
/* margin-inlineを使った書き方 */
.element {
margin-inline: auto;
}
🤮 Bad…
.element {
margin-left: auto;
margin-right: auto;
}
.element {
margin: 0 auto;
}
place-content: center
横幅を指定しない要素の左右中央寄せはplace-content: center
を使います。
🥳 Good!!
.parent {
display: grid;
place-content: center;
}
🤮 Bad…
.parent {
display: flex;
justify-content: center;
align-items: center;
}
width: fit-content
width: fit-content
を指定すると自身の横幅が子要素の横幅と同じ値になります。つまり、width
に固定値を指定しなくてもmarign-inline: auto
などで中央配置できるようになります。
🥳 Good!!
/* ひとつの要素で中央配置が可能に! */
.target {
width: fit-content;
marign-inline: auto;
}
🤮 Bad…
.parent {
text-align: center;
}
.target {
display: inline-block;
}
word-break
文字列がはみ出ないように折り返しをword-break: break-word
で制御している方は多いと思いますが、現在は非推奨です。
文字列の折り返しについてはICSさんの記事で丁寧に解説されているので是非ご覧ください。
🥳 Good!!
body {
overflow-wrap: anywhere;
word-break: normal;
line-break: strict;
}
🤮 Bad…
body {
word-break: break-word;
}
transform
transform
プロパティのtranslate
やrotate
は独立プロパティになったので、以下のように指定できます。
.element {
translate: 10px;
scale: 1.5;
rotate: 45deg;
}
複数の変形を行っている場合の記述も簡潔になります。
🥳 Good!!
.icon {
translate: 10px;
rotate: 45deg;
}
a:hover .icon {
rotate: 90deg;
}
🤮 Bad…
.icon {
transform: translate(10px) rotate(45deg);
}
a:hover .icon {
transform: translate(10px) rotate(90deg);
}
transition
transition
プロパティを使う時はアニメーションを適用させたいプロパティを必ず指定します。
プロパティを指定しないでtransition: all 0.3s
のようにすると全てのプロパティにアニメーションが適用されるので、ページ読み込み時やレスポンシブ時に変な挙動になることがあります。
🥳 Good!!
.fadein {
transition: opacity 0.3s;
}
🤮 Bad…
.fadein {
transition: 0.3s;
}
filter
filter
プロパティを使うことで、画像をぼかしたり暗くしたりすることができます。hover時に画像をぼかすような処理も、ぼかし用の画像に切り替えるのではなくCSSだけで完結するので、画像が運用時に変わってもぼかし用の画像作成が不要になります。
.photo {
filter: blur(10px);
}
clip-path
三角形などの図形を描画するにはclip-path
プロパティを使います。三角形を作るにはborder
を使った昔ながらの手法がありますがclip-path
のほうが直感的に扱えます。
🥳 Good!!
.triangle {
clip-path: polygon(100% 50%, 0 0, 0 100%);
width: 100px;
height: 100px;
background-color: red;
}
🤮 Bad…
.triangle {
width: 0;
height: 0;
border-style: solid;
border-width: 100px 0 100px 173.2px;
border-color: transparent transparent transparent red;
}
便利なジェネレーターもあります。
currentColor
currentColor
を値として指定すると、現在のcolor
プロパティの値が参照されます。
以下のようなボタンの実装例を用意しました。currentColor
を使うことで、hover時のsvgの色指定を省略できます。
HTML
<a class="button" href="">
<span>BUTTON</span>
<svg ... /> // 矢印アイコン
</a>
🥳 Good!!
.button {
color: white;
/* ...略 */
}
.button:hover {
color: blue;
/* ...略 */
}
.button svg {
fill: currentColor; /* .buttonのcolorを参照しているので、通常時はwhite、hover時はblueになる */
}
🤮 Bad…
.button {
color: white;
/* ...略 */
}
.button:hover {
color: blue;
/* ...略 */
}
.button svg {
fill: white;
}
.button:hover svg {
fill: blue;
}
clamp()
clamp関数はvw
などの動的な値に対して最大(最小)値を設定できます。
例えば、フォントサイズにvw
を指定すると大きく(小さく)なりすぎることがありますが、clamp関数を使うことで最大(最小)の文字サイズを指定できるようになります。ブレイクポイントでvw
の値を変えるより直感的に扱えます。
🥳 Good!!
.text {
font-size: clamp(16px, 5vw, 20px); /* ベースサイズは5vw、最小16px、最大20px */
}
🤮 Bad…
.text {
font-size: 5vw;
}
@media (max-width: 767px) {
.text {
font-size: 8vw;
}
}
便利なジェネレーターもあります。
svh
要素の高さを画面いっぱいにするには100vh
ではなく100svh
を指定します。vh
はiOSのアドレスバーの高さを含んでしまうので「画面の高さ+アドレスバーの高さ」になってしまいますが、svh
はアドレスバーの高さを含まない純粋な「画面の高さ」のみを取得できます。
.main-visual {
height: 100svh;
}
border-radius: 100vmax
完全な角丸のボタンを実装する時のborder-radius
には9999px
などの大きい数値を指定するのではなく100vmax
を指定することで、ボタンがどんな大きさになっても完全な角丸を保てるようになります。
🥳 Good!!
.button {
border-radius: 100vmax;
}
🤮 Bad…
.button {
border-radius: 9999px;
}
@media (min-width: 768px) & range記法
昨今のブラウザではメディア種別のscreen
を省略しても「画面」と認識してくれるので、メディアクエリのscreen and
は省略しても問題ありません。
🥳 Good!!
@media (min-width: 768px) {
.element { ... }
}
🤮 Bad…
@media screen and (min-width: 768px) {
.element { ... }
}
また、range記法という記述方法も2023年にリリースされました。
@media (width <= 768px) {
.element { ... }
}
iOS Safariのバージョン16.3以下がサポート外なので、使う場合はコンパイラを挟むことを推奨します。
any-hover: hover
スマホやタブレットなどタップで操作をする端末ではhover処理は無効にします。
タップデバイスを判定するにはメディア特性のany-hover: hover
を使います。昨今は小さいノートパソコンや大きいスマホなどがあるので、画面幅で判定するのはよろしくありません。
🥳 Good!!
@media (any-hover: hover) {
.button:hover {
background-color: red;
}
}
🤮 Bad…
@media (min-width: 768px) {
.button:hover {
background-color: red;
}
}
prefers-reduced-motion: reduce
メディア特性のprefers-reduced-motion
を使うことで、デバイス設定で「視差効果を減らす」が有効かどうかを判定できます。
ユーザーは過度なアニメーションを求めていない場合もあるので、ユーザー側でアニメーションのON/OFFを選択できるように実装してあげることが大切です。
以下は「視覚効果を減らす」が有効化されている時に、アニメーション時間を極限まで短くする例です。
@media (prefers-reduced-motion: reduce) {
*,
::before,
::after {
transition-duration: 1ms !important;
animation-duration: 1ms !important;
animation-iteration-count: 1 !important;
}
}
Visually Hidden
Visually Hiddenとは、視覚的には要素を非表示にしたいけど、スクリーンリーダーには読み上げてもらいたい時に使うCSSスニペットです。
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
clip-path: inset(50%);
white-space: nowrap;
border: 0;
}
ラジオボタンやチェックボックスのinput
要素を非表示にしてスタイリングする際は、display: none
ではなくVisually Hiddenを使います。display: none
でinput
要素自体を消してしまうとフォーカスが当たらないなどの弊害が生じます。
🥳 Good!!
[type="radio"] {
/* visually-hiddenのスタイル */
}
🤮 Bad…
[type="radio"] {
display: none;
}
親要素の左右にpaddingが指定されている状態で子要素の横幅を画面幅と同じにするレイアウト手法
親要素の左右にpadding
が指定されている状態で、子要素の幅を画面幅と同じにする場合はcalc
とvw
を使って実装します。
🥳 Good!!
.wrapper {
padding-left: 40px;
padding-right: 40px;
}
.photo {
width: 100vw;
margin-inline: calc(50% - 50vw);
}
従来の書き方だと、以下のようにpadding
の値に応じて子要素のwidth
やmargin
の値も変わってしまいます。これだと、レスポンシブ時にpadding
の値が変わったらwidth
やmargin
も変える必要がありますが、calc
とvw
を使うことで再定義が不要になります。
🤮 Bad…
.wrapper {
padding-left: 40px;
padding-right: 40px;
}
.photo {
width: calc(100vw + 80px); /* 80px = 左右のpaddingの合計値 */
margin-left: -40px; /* ネガティブマージンで要素を左に移動させる */
}
コンテンツ幅から片方だけ画面の端まではみ出しているレイアウト手法
このようなレイアウトもcalc
とvw
を使うことで効率よく実装できます。
.片方だけはみ出させる要素(左配置の場合) {
width: 50vw;
margin-left: calc((50vw - 50%) * -1);
}
.片方だけはみ出させる要素(右配置の場合) {
width: 50vw;
margin-right: -50vw;
}
/* 反対側の要素には`width: 50%`を、これらの親要素には`display: flex`を指定します */
詳しくはCodepenをご覧ください。
メインコンテンツが少ない状態でもフッターを画面最下部に固定させるレイアウト手法
コンテンツ量が少なくてもフッターを画面最下部に固定するレイアウト手法です。
body {
min-height: 100dvh;
}
footer {
position: sticky;
top: 100%;
}
3. JavaScript
JavaScriptも画像と同様にパフォーマンスに影響を与えやすい項目なので、ファイルの読み込み方やスクロール時の処理の実装方法などをまずは覚えることをおすすめします。
Defer
<script>
にdefer
属性を付けると非同期でJSファイルがダウンロードされます。また、ダウンロード開始をできるだけ早くしたいので</body>
の手前ではなく<head>
のできるだけ上のほうで読み込ませます。
🥳 Good!!
🤮 Bad…
...
<script src="script.js">
</body>
DOMContentLoaded
defer
でJSを読み込む場合、HTMLが全て読み込まれる前にJSが実行されることがあります。そのため、要素の取得ができずにエラーになることがあるのでDOMContentLoaded
イベントの中で処理を実行します。
🥳 Good!!
window.addEventListener('DOMContentLoaded', () => {
// ここにページ読み込み時の処理を書く
});
🤮 Bad…
(() => {
// ここにページ読み込み時の処理を書く
})();
Debounce
スクロールイベントやリサイズイベントは実行される頻度が極端に高いので、ブラウザに負荷がかかり画面がカクカクする原因になります。なので、Debounceという手法で実行頻度を減らしてあげます。
debounce関数
function debounce(func, timeout) {
let timer;
timeout = timeout !== undefined ? timeout : 300; // funcが呼び出されるまでの遅延時間
return () => {
const context = this;
const args = arguments;
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(context, args);
}, timeout);
};
}
以下はリサイズ時にヘッダーを取得する例です。
🥳 Good!!
const getHeader = () => document.querySelector('header');
const debouncedFunction = debounce(getHeader)
window.addEventListener('resize', debouncedFunction, false);
🤮 Bad…
const getHeader = () => document.querySelector('header');
window.addEventListener('resize', getHeader, false);
Intersection Observer
前項でも書いた通り、スクロールイベントは負荷が高いのであまり使いたくありません。Intersection Observerを使うことで、ブラウザに負荷をかけずにスクロールに応じた処理を実装できます。
以下はスクロールアニメーションのサンプルで、data-scroll-anima
属性を持つ要素が画面の上下20%の位置までスクロールされたら属性値がtrue
になります。
JavaScript
// 監視対象要素
const animaElements = document.querySelectorAll("[data-scroll-anima]");
// 交差時に実行される関数
const doWhenIntersect = entries => {
const entriesArray = Array.prototype.slice.call(entries, 0);
entriesArray.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.dataset.scrollAnima = 'true';
}
});
}
// IntersectionObserverのオプション
const options = {
root: null,
rootMargin: '-20% 0px -20% 0px', // 要素が画面の上下20%を超えたら監視する
threshold: 0
};
// 対象要素の数だけobserverで監視
const observer = new IntersectionObserver(doWhenIntersect, options);
animaElements.forEach((box) => {
observer.observe(box);
});
CSS
[data-scroll-anima] {
opacity: 0;
transition: opacity .3s;
}
[data-scroll-anima="true"] {
opacity: 1;
}
matchMedia
ブレイクポイントに応じて処理を実行する場合、画面幅をリサイズイベントで監視すると前項で書いた通りブラウザに負荷がかかるので、代わりにmatchMediaを使って今のブレイクポイントを判定します。
また、CSS変数にブレイクポイントを指定しておくことで、CSS側でブレイクポイントの値が変わってもJS側での修正は不要になります。
CSS
:root {
--breackpoint-md: 768px;
}
JavaScript
// ブレイクポイントの値をCSS変数から取得して、matchMediaにセット
const rootStyles = getComputedStyle(document.documentElement);
const breackpointMd = rootStyles.getPropertyValue('--breackpoint-md');
const mediaQueryList = window.matchMedia(`(max-width: ${breackpointMd})`);
// ブレイクポイントに応じて実行する処理
const mediaQueryFunction = (event) => {
if (event.matches) {
console.log('768px以下です');
} else {
console.log('769px以上です');
}
};
// ブレイクポイントが変わった時のイベントを登録
mediaQueryList.addEventListener('change', mediaQueryFunction);
// ページ読み込み時のイベントを登録
window.addEventListener('DOMContentLoaded', () => mediaQueryFunction(mediaQueryList));
Sassでブレイクポイントの変数を定義している場合、以下のようにCSS変数を登録をすれば上記と同じことができます。
Sassの例
$breackpoint-md: 768px;
:root {
--breackpoint-md: #{$breackpoint-md};
}
375px未満のレスポンシブ対応
幅320pxのような小さい端末のレスポンシブ対応はCSSで頑張るのではなく、Viewportで表示倍率を縮小します。
昨今のデザインは375pxで作られることが多く、そもそも320px程度まで考慮されていない場合が多いのでCSSで調整するには限界があります。なので、表示倍率を縮小することで実装工数が大幅に削減でき、大量のメディアクエリの記述も発生しなくなります。
const adjustViewport = () => {
const triggerWidth = 375;
const viewport = document.querySelector('meta[name="viewport"]');
const value = window.outerWidth < triggerWidth
? `width=${triggerWidth}, target-densitydpi=device-dpi`
: 'width=device-width, initial-scale=1';
viewport.setAttribute('content', value);
}
const debouncedFunction = debounce(adjustViewport) // debounce関数は、Debounceの項で解説した関数です
window.addEventListener('resize', debouncedFunction, false);
target-densitydpi=device-dpi
は、数年前にAndroidの4系か6系の一部の端末でViewportが正しく変らない事象が起きて、その対処法として付けていました。
昨今の環境でも必要かどうかはちゃんと調べられていないので、これの有無はご自身で判断してください。正直あってもなくても挙動は変らないと思いますが、あることによって特定の環境でも崩れないのであれば付けたままでもいいと私は思っています。
ES6以降の記法
JavaScriptはES6(ES2015)以降、便利な機能や構文が数多く追加されました。ここからはES6以降に追加されたWeb制作寄りの内容を少し紹介していきます。
文字列の結合
テンプレートリテラルを使うことで、変数と文字列の結合が楽になります。
🥳 Good!!
const message = `私は${name}です。`;
🤮 Bad…
const message = '私は' + name + 'です。';
配列操作
配列に関するメソッドはかなり追加されました。新しい配列を生成するmap
、特定の配列を探すfind
、配列の有無を確認するsome
など、これまではfor
文で行っていた処理をこれらのメソッドを使うことで記述量が圧倒的に短くなります。
// この中からidが2のデータを検索する
const users = [
{ id: 1, name: '山田' },
{ id: 2, name: '田中' },
{ id: 3, name: '中村' }
];
🥳 Good!!
const targetUser = users.find(user => user.id === 2);
🤮 Bad…
let targetUser;
for (let i = 0; i < users.length; i++) {
if (users[i].id === 2) {
targetUser = users[i];
break;
}
}
スプレッド構文
スプレッド構文を使うことで、配列やオブジェクトの結合や展開が楽になります。
let arr1 = [1, 2, 3];
let arr2 = [4, 5];
🥳 Good!!
// 配列の結合
let combined = [...arr1, ...arr2]; // [1, 2, 3, 4, 5]
// 配列のコピー
let arrCopy = [...arr1]; // [1, 2, 3]
🤮 Bad…
// 配列の結合
let combined = arr1.concat(arr2); // [1, 2, 3, 4, 5]
// 配列のコピー
let arrCopy = arr1.slice(); // [1, 2, 3]
Async / await
特定の処理の後に他の処理を実行する場合は Async / await を使います。setTimeout
で遅延させると、必ずしも遅延させた秒数で手前の処理が終わるとは限らないので絶対に辞めましょう。
🥳 Good!!
// データを取得(取得に時間がかかる)
async function fetchData() {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
}
async function run() {
try {
const data = await fetchData(); // awaitを使うことでfetchData()が完了するまで次の行の処理を待つ
console.log('取得したデータ:', data);
} catch (error) {
console.error('エラーが発生しました:', error.message);
}
}
run();
🤮 Bad…
function fetchData() {
// 同様の処理
}
async function run() {
const data = fetchData(); // fetchData()の完了を待たずに次の行を実行してしまう
// setTimeoutで処理を遅延させているが、fetchData()の完了が2000ミリ秒以内に終わる保証はないため、dataが空の状態でconsole.logが実行される可能性がある
setTimeout(() => {
console.log('取得したデータ:', data);
}, 2000);
}
run();
Fetch / Axios
APIなど外部からデータを取得する時はFetch
かAxios
を使います。昔はXMLHttpRequest
やjQueryのAjax
を使っていましたが、Fetch
やAxios
のほうが例外処理やデータの扱いに優れています。
🥳 Good!!
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
🤮 Bad…
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/data');
xhr.onload = function() {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error('APIエラー');
}
};
xhr.onerror = function() {
console.error('ネットワークエラー');
};
xhr.send();
最後に
かなりの量を紹介したので一度に全部を使いこなすのは難しいと思います。個人的にこれだけは…をいくつかピックアップしたので、まずはそれだけでも取り入れてみてください。
- 1. 画像
- Lazy loadingで遅延読み込みをして、画像の出し分けはPicture要素を使う
- 背景画像ではなくImg要素で読み込み、要素いっぱいに広げる時は
object-fit
、縦横比を制御するにはaspect-ratio
を使う
- 2. レイアウト
- 格子状のレイアウトはGrid Layoutを、Flexboxの間隔は
gap
を使う - 状況に応じて
calc()
とvw
を組み合わせてレイアウトを組む
- 格子状のレイアウトはGrid Layoutを、Flexboxの間隔は
- 3. JS最適化
- JSファイルは
Defer
で読み込み、処理はDOMContentLoaded
イベント内で行う - スクロールやリサイズのイベントは高負荷なので、Debounceで実行回数を間引く
- スクロール時の処理は
Intersection Observer
を使う
- JSファイルは