【ES6】タブ切り替えをWAI-ARIAに対応させる

今回は前回作成したタブ切り替えメニューをWAI-ARIAに対応させていきます。

実際のコード


See the Pen
WAI-ARIA対応タブメニュー
by naomi homma (@naomi-homma)
on CodePen.

HTMLの実装:role属性とaria属性の追加

<div class="tab">
 <ul class="tab__list" role="tab-list">
  <li class="tab__item">
   <button class="tab__button" type="button" role="tab" aria-controls="tab-panel-1" aria-selected="true">tab01</button>
  </li>
  <li class="tab__item">
   <button class="tab__button" type="button" role="tab" aria-controls="tab-panel-2" aria-selected="false">tab02</button>
  </li>
  <li class="tab__item">
   <button class="tab__button" type="button" role="tab" aria-controls="tab-panel-3" aria-selected="false">tab03</button>
  </li>
 </ul>
<div class="tab__content">
 <div class="tab__content-item" id="tab-panel-1" role="tab-panel" aria-hidden="false">
  <p class="tab__content-text">tab01の本文です。</p>
 </div>
 <div class="tab__content-item" id="tab-panel-2" role="tab-panel" aria-hidden="true">
  <p class="tab__content-text">tab02の本文ですよ!!tab02の本文ですよ!!</p>
 </div>
 <div class="tab__content-item" id="tab-panel-3" role="tab-panel" aria-hidden="true">
  <p class="tab__content-text">tab03の本文ですか??</p>
 </div>
</div>

role属性とは?

role属性は、コンテンツの役割を明示します。
今回は、どの要素がタブエリア・タブ・パネルに該当するかを明示するため以下を記述しました。

  • ul要素へrole="tab-list"
  • タブ部分となるbutton要素にrole="tab"
  • パネル部分のdiv要素にrole="tab-panel"

aria属性とは?

aria属性は、要素に状態や性質を明示させます。
今回は、タブとパネルの紐付け、タブの選択状態、パネルの表示・非表示状態を明示するため以下を記述しました。

  • タブとパネルの関連性を示すためタブへaria-controls属性を、パネルへid属性を指定
  • タブの選択状態を示すために、aria-selectedを真偽値で指定
  • パネル部分の表示・非表示の状態を伝えるためにaria-hidden属性を真偽値で指定

CSSの実装

.tab__button {
  display: block;
  font-size: 25px;
  padding: 0.5rem 1rem;
  cursor: pointer;
  border-bottom: 3px solid blue;
  opacity: 0.3;

  &[aria-selected='true'] {
    opacity: 1;
  }
}

.tab__content-item {
  display: block;

  &[aria-hidden='true'] {
    display: none;
  }
}

class 属性を使わず、aria-* 属性をセレクターとして指定しています。

JavaScriptの実装

/* -----------------------------------------
*   Script for 01
----------------------------------------- */
document.addEventListener('DOMContentLoaded', () => {
  function tabClick(e) {
    //クリックされたtabの親要素を取得
    const tabList = e.currentTarget.closest('.tab__list')
    //クリックされたtabのaria-controlsの値を取得
    const id = e.target.getAttribute('aria-controls')
    //クリックされたtabと同じ親要素を持つパネルを取得
    const contentList = document
      .querySelector(`#${id}`)
      .closest('.tab__content')

    //表示状態のボタン、パネルを取得
    const active_tabItems = tabList.querySelector('[aria-selected="true"]')
    const active_tabContentItems = contentList.querySelector(
      '[aria-hidden="false"]'
    )

    //タブボタンを非アクティブ
    active_tabItems.setAttribute('aria-selected', 'false')
    //タブパネルを非表示
    active_tabContentItems.setAttribute('aria-hidden', 'true')

    //選択されたtabを表示
    e.target.setAttribute('aria-selected', 'true')

    //選択されたtabに紐づくpanelを表示
    document.querySelector(`#${id}`).setAttribute('aria-hidden', 'false')
  }
  //全てのtabにイベントリスナー設定
  const tabs = document.querySelectorAll('.tab__item')
  tabs.forEach((tab) => {
    tab.addEventListener('click', tabClick)
  })
})

今回は以上になります!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

レスポンシブwebデザイン注意すべきポイント

先日、「HOW TO MAKE A WEBSITE REAPONSIVE」というタイトルのツイートを見つけました。

webサイトをレスポンシブにする上で、16(くらい)の注意点が挙げられていました。
基本的な内容ですが、大事なことが書かれていたので英語の勉強も兼ねて翻訳してみました!

In order to make a RWD, you just need to consider one thing.

"Ability of content to fit inside any device that look good and it will be for user to interact with that"

{ 2 / 16 }

RWDを作るために、あなたはただ一つのことを考慮する必要があります。
「見栄えの良いデバイスにコンテンツを収めることができ、ユーザーがそれを操作できるようになります」

Responsive web design is not a kind of program or framework. We can say it's a combination of various concepts using which we try to make our web page look good on all devices

The great thing is that you can achieve RWD using HTML and CSS only

{ 3 / 16 }

レスポンシブウェブデザインは一種のプログラムやフレームワークではありません。これは、すべてのデバイスでWebページの見栄えを良くするために使用するさまざまな概念の組み合わせであると言えます。
すばらしいのは、HTMLとCSSのみを使用してRWDを実現できることです。

First and foremost thing in order to make RWD is viewport element.

It forces page to follow the screen-width of the device.

<meta name="viewport" content="width=device-width, initial-scale=1.0">

{ 4 / 16 }

RWDを作成するための何よりも重要なことは、ビューポート要素です。
ページをデバイスの画面幅に強制的に従わせます。


The second important point to note is that don't use large fixed width or height element.

It can cause trouble. Let's say an element having width 500px but user is viewing on a device having width smaller than 500px

In such cases, use min-width or max-width

{ 5 / 16 }

注意すべき2番目の重要な点は、大きな固定幅または高さ要素を使用しないことです。
トラブルの原因になります。幅が500pxの要素で、ユーザーが幅が500px未満のデバイスで表示しているとします。
このような場合は、min-widthまたはmax-widthを使用してください

Use HTML tag

The HTML element allows you to define different images for different browser window sizes.

{ 6 / 16 }

HTMLのタグを使用する
HTMLの要素を使用すると、ブラウザのウィンドウサイズごとに異なる画像を定義できます。

Responsive text size

Although you can make text responsive using media queries but you can also use "viewpoet" width as well.

h1 {
  font-size: 10vw;
}

{ 7 / 16 }

レスポンシブテキストサイズ

メディアクエリを使用してテキストをレスポンシブにすることはできますが、「viewpoet」幅を使用することもできます。

Try to use Layouts

Using Grid or Flex layout always beneficial in order to make a web page responsive. Both these layout are not hard to learn. Try to use them.

{ 8 / 16 }

レイアウトを使用してみてください

グリッドまたはフレックスレイアウトを使用すると、Webページをレスポンシブにするために常に有益です。これらのレイアウトはどちらも習得するのは難しくありません。それらを使用してみてください

Use box-sizing: border-box

Don't know you consider with point valid or not but small thing can make big impact.

Box-sizing: border-box; forces an element to adjust it's padding and border inside width and height of that element.

Even small 4px border can damage the responsiveness. Hence I always consider box-sizing border-box for RWD.

{ 9,10 / 16 }

ボックスサイズを使用する:border-box

ポイントが有効かどうかはわかりませんが、小さなことが大きな影響を与える可能性があります。

box-sizing:border-box;
要素に、その要素のパディングと境界線の内側の幅と高さを調整するように強制します

小さな4pxの境界線でさえ、レスポンシブを損なう可能性があります。したがって、RWDではボックスサイズのボーダーボックスを常に検討します。

Media Queries are saviour

Media query is a rule to include a block of CSS properties only if a certain condition is true. It is very useful in order to make a RWD.

{ 11 / 16 }

メディアクエリは救世主です

メディアクエリは、特定の条件が真である場合にのみCSSプロパティのブロックを含めるルールです。 RWDを作るのにとても便利です。


Here is the basic syntax of media query

Check out the detailed thread on media query Down-pointing triangle

{ 12 / 16 }

メディアクエリの基本的な構文は次のとおりです

@media screen and (max-width: 480px) {
  .element {
    width: 100px;
  }
}

Use "auto" in media

Almost 99% a web page contains images or videos. And in order to make them responsive, use "auto"

If the width property is set to a percentage and the height is set to "auto", the image will be responsive

{ 13 / 16 }

mediaで「auto」を使用する

ほぼ99%のWebページに画像またはビデオが含まれています。そして、それらをレスポンシブに対応させるために、「auto」を使用してください

widthプロパティがパーセンテージに設定され、heightが「auto」に設定されている場合、画像はレスポンシブになります。

In order to improve further, you can use max-width in image so that quality of image will be persist. After all it can also be considered as responsive

{ 14 / 16 }

さらに改善するために、画像の最大幅を使用して、画像の品質が維持されるようにすることができます。結局のところ、それはレスポンシブであると見なすこともできます。

Use frameworks if possible

Sometimes it might be a tedious task to handle responsiveness if you're wirting pure CSS. Try to use frameworks because they can save a lot of time designing a responsive website

{ 15 / 16 }

可能であればフレームワークを使用する

純粋なCSSを使用している場合、レスポンシブを制御することが面倒な作業になることがあります。レスポンシブウェブサイトの設計に多くの時間を節約できるため、フレームワークを使用してみてください。(Tailwindなど...)



基本的な内容でしたが、再確認できて良かったです。
英語も楽しいですね、これからも細々と勉強を続けていこうと思います。

今回は以上です!

↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【JavaScript】タブ切り替えメニュー:ページ内に複数設置可能

だいぶ久しぶりの更新となってしまいましたが、
今回はJavaScriptでTabメニューの実装をしていきます。

仕様

  • タブは3つ以上ある
  • クリックすると切り替わる
  • 表示されているタブは色が付く
  • 同じタブモジュールをページ内に複数設置することが可能

ポイントとしては、「同じタブモジュールをページ内に複数設置することが可能」という部分ですね。
実装にあたりいろいろググったのですが、JavaScriptで複数設置を想定した記事が少なく、
その条件をクリアするのが難しかったです。

また、tabと本文についてはJavaScriptのカスタムデータ属性を使い紐付けています。

実際に書いたコード


See the Pen
【JavaScript】タブメニュー
by naomi homma (@naomi-homma)
on CodePen.

コードの解説

JavaScriptのポイント

まずは、全てのtabにイベントリスナーを設定します。
この時配列用メソッドが使えるように「document.querySelectorAll」で要素の取得を行います。
「querySelectorAll」は「NodeList」を返しforEachメソッドが使えます。

developer.mozilla.org

要素の取得方法についてはこちらを参考にさせて頂きました。
qiita.com

//全てのtabにイベントリスナー設定
  const tabs = document.querySelectorAll('.tab__item')
  tabs.forEach((tab) => {
    tab.addEventListener('click', tabClick)
  })

次にtabと本文の表示切り替えを行うtabClick関数を書いていきます。

function tabClick(e) {
    //クリックされたtabのカスタムデータ属性の値を取得
    const tabTargetData = e.currentTarget.dataset.tab
    //クリックされたtabの親要素及びその子要素を取得
    const tabList = e.currentTarget.closest('.tab__list')
    const tabItems = tabList.querySelectorAll('.tab__item')
    //クリックされたtabの親要素と同じ親要素を持つcontentを取得(兄弟要素)
    const tabContentItems = tabList.nextElementSibling.querySelectorAll(
      '.tab__content-item'
    )
 ...途中略
  }

ここでポイントとなるのが、イベントが発生したtabの親要素を起点に
子要素・兄弟要素を取得し、それらに対してactiveクラスの付け外しを行うというところです。

モジュールを複数設置しても問題なく動かすにはブロックを分ける必要があります。

 const tabList = e.currentTarget.closest('.tab__list')

このように記述してある箇所を

 const tabList = document.querySelectorAll('.tab__list')

上記のように記述すると、モジュールを複数設置した場合に、
全てのモジュールの「.tab__list」が取得されてしまい、想定と異なる動きになってしまいます。


続いて、イベントが発生したtabと同階層にある全てのtab、
及びそれに紐づく全てのcontentから一旦activeクラスを外します。

それから、イベントが発生したtabにのみactiveクラスを付与します。
これで表示されているtabにのみ色をつけることができます。

//クリックされたtabと同階層のtab及びcontentからactiveクラスを削除
    tabItems.forEach((tabItem) => {
      tabItem.classList.remove('is-active')
    })
    tabContentItems.forEach((tabContentItem) => {
      tabContentItem.classList.remove('is-active')
    })
//クリックされたtabにactiveクラスを付与
    e.currentTarget.classList.add('is-active')

最後に、イベントが発生したtabが持つカスタムデータ属性の値と
等しい値を持つcontentを表示させることで完成です。

//取得したtabのデータ属性の値と等しい値を持つコンテンツにactiveクラスを付与
    tabContentItems.forEach((tabContentItem) => {
      if (tabContentItem.dataset.content === tabTargetData) {
        tabContentItem.classList.add('is-active')
      }
    })

HTMLのポイント

tab部分はクリックできることを明示的に示すためにbuttonタグで実装しています。

さらに今回のロジックで重要になるカスタムデータ属性をtabと本文に設定します。
developer.mozilla.org

<div class="tab">
 <ul class="tab__list">
  <li class="tab__item is-active" data-tab="01">
   <button class="tab__text" type="button">tab01</button>
...途中省略
</div>
<div class="tab__content">
 <div class="tab__content-item is-active" data-content="01">
  <p class="tab__content-text">tab01の本文です。</p>
 </div>
...途中省略
</div>

終わりに

今回はtabとcontentの紐付けにカスタムデータ属性を使用しました。
また、モジュールを複数設置できるように実装を行いました。

ただ、アクセシビリティーの観点から見ると改善の余地があるので
次回はこのコードを元にWAI-ARIAに対応させていきたいと思います。

今回は以上になります!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

2020年振り返りと2021年の目標

だいぶ久しぶりのブログとなってしまいました...

2020/10/5にエンジニアとして入社してから、実力不足を痛感し現場についていくための勉強を優先させておりました。
(ブログでのアウトプットは非常に大事なんですけどね!心の余裕が...)

というわけで早速、2020年を振り返ってみようと思います。

なんとかエンジニアへのジョブチェンジを達成!!

今年はこの一言に尽きると思います。
1月末に前職を退職し、そこからプログラミングの勉強をスタート。
最初はスクールに通う選択肢もあり、某スクールの無料相談にも行ってみましたが、
話を聞く限りある程度理解してからの方が良いのかなと思い、まずは独学で進めることにしました。

そんなこんなでコロナが流行り始め、対面での授業もできなくなったため
そのまま独学で勉強を継続することに決めました。

ひたすらにプログラミングの勉強

仕事を退職したので、普段業務を行っている時間は少なくとも勉強することにしました。
(それでも全然足りないよ!)
世間はコロナで生活様式の変化が起こっていましたが、私は変わらず引きこもり勉強....
既にWeb業界で活躍していた友人におすすめされた、Progate・ドットインストールから。
もちろんどちらも有料会員でしっかりと基礎固めを行いました。

実は最初はrubypythonを勉強していました笑
思った通りにプログラムが動くことに感動し楽しかったのですが
転職するには難しいのでは?と感じ、フロントエンドに路線変更を行ったという経緯があります。

また、勉強の開始と共に、情報収集の目的でtwitterを始めました。
SNSは得意でなかったのですが、本当に始めてよかったです!
この行動を取った、当時の私、good job!と言ってあげたいです。
きっかけは忘れました笑

twitterで出会った方に励まされ、低迷期を乗り切る

twitteを始めてからは、「#今日の積み上げ」として学習記録を呟くようにしていました。
他の初学者の積み上げを見て、自分を鼓舞していましたね...
本当皆さん凄すぎなんですよね...比べて落ち込むこともよくあり笑

そんな状況で一人で勉強を続けていると、うまくいかずに前に進めなかったり
本当にこのままで良いのかと不安にかられる時が定期的に訪れました。

その思いをそのままtwitterに呟いたところ、たくさんの方から励ましのお言葉を頂きました。
なんて暖かい世界なんだ...

その言葉に救われて復活し、勉強を継続できました。

友人の会社で修行&もりけん塾に入塾


独学開始から約4ヶ月たった頃、もう少しレベルをあげたいなと思い始め
友人の会社で修行させてもらうことにしました。
(友人ありがとう...本当に感謝です)

一緒に作業をしたエンジニアさんが、フロントもバックエンドもどちらもできるつよつよで、
本当に勉強になりました。

最初はエンジニアさんのいうことが全く理解できなかったのですが(今でも...)
こんな風に業務が進んでいくんだなというのがイメージできました。
そして、GitHubで初めてプルリクを出す経験をしました笑

友人の会社での修行を終えた頃、twitterで見つけたもりけん塾に入塾。
JavaScript、Reactを中心にレビューを頂いたり、転職活動の相談にも乗って頂きました。

就職活動開始から2ヶ月弱で内定を頂く

お盆明けからWantedlyとFind job!を使って転職活動を行いました。
コロナ禍で求人数がそもそも少ない上に、未経験というハンデを持った状態ではありましたが
数社は面接の機会を頂くことができました。

twitter・ブログでの発信、ポートフォリオ、これまでの経歴(研究職)に対して評価頂いた印象でした。

そしてありがたいことに、現在の会社に内定を頂きました。

入社してから実力不足を痛感し、さらに勉強の日々

覚悟はしておりましたが、入社してから自分の出来なさ加減に悔しい思いをする日々でした。
同じ時期に未経験で入社された方々にどんどん差を付けられてしまい...

ただ、教育担当の先輩が忙しくても丁寧に質問に答えてくださるので
早くスキルを身に付けて恩返しがしたい、という気持ちで頑張っています。



2020年はこんな感じで激動の1年となりました。
とにかく、目標としていたエンジニアとして転職できたのは本当によかったです。
関わって頂いた皆さんには本当に感謝してもしきれません。


2021年の目標

2021年は、まずは「安心して任せてもらえるようなエンジニア」になりたいというのが一番ですね。
まだまだ、実力不足で先輩方にご迷惑をおかけしてしまっている状況なので、
少しでも力になれるように積極的に行動していきたいなと思っています。

ただ、この積極的って難しいなぁと....
具体的にどんなことをしていくべきなのか。

まずは、今必要な技術・行動が何なのかを深掘りして考えていく習慣が必要ですね。
(これはチームリーダーに言われた言葉です)
そしてそれを言語化する。

この過程ができていなかったので、時間のかかる作業ですが習慣化していきたいです。

他には...

・コーディングスキルの向上
・技術ブログを書く(月2回は...)
・社外でエンジニア仲間を作る
twitterで色んな方とお話をして、オフラインでも会いたい!

などなど...

2021年はエンジニアとして飛躍の年にしたいなと思っています!
上には上がいますが、まずは目の前の業務をしっかりこなせるように
失敗を恐れずに、そして焦らずに着実に前に進んでいきたいと思います。

【Visual Studio Code】"Extension host terminated unexpectedly."への対処法

VSCodeを立ち上げた際に以下のメッセージが出て作業できないことがありました。

「Extension host terminated unexpectedly.」

発生する時としない時があり、何が原因か分かりませんでした。

VSCodeを一度閉じて、再起動すると問題なく操作できる時もあったのですが、
立て続けに起こったので対応策を探してみました。

Live Sass Compilerをアンインストールする

visual studio code - Extension host terminated unexpectedly - Stack Overflow

こちらのサイトにいくつかの解決策が記載されていました。
私のVSCodeには拡張機能として「Live Sass Compiler」をインストールしていたので
これをアンインストールしたところ、問題なく起動できました。

拡張機能のアンインストール方法

f:id:hnm-n-1029:20200917112707p:plain

①左側サイドバーの□マークをクリック

②上部検索窓で拡張機能の名称を入力
 ※今回は「sass」で検索しています。

③アンインストールしたい拡張機能を選択

④「Uninstall」をクリック

これでアンインストールの完了です。

これで完全解決したかは分からないのですが、今後またこの現象が発生したら追記していきたいと思います。

今回は以上です!



↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【TypeScript】既存のReactアプリ(create-react-app)にTypeScriptを導入する方法

今回は、create-react-appで構築したアプリへTypeScriptを導入する方法を解説していきます。
※作成済みのアプリがある前提となります。

ちなみにcreate-react-appの場合、最初からTypeScriptのサポートが含まれているので、
環境構築の際に以下コマンドを実行することでTypeScriptを導入することが可能です。

//my-appはプロジェクト名(任意)
npx create-react-app my-app --template typescript

今回は、既存のcreate-react-appプロジェクトへTypeScriptを導入方法になります。

以下のサイトを参考に導入を進めていきます。
静的型チェック – React

Adding TypeScript

①TypeScriptの導入(インストール)

まずは以下のコマンドを実行し、TypeScriptをインストールします。

npm install --save typescript @types/node @types/react @types/react-dom @types/jest
# or
yarn add typescript @types/node @types/react @types/react-dom @types/jest

②ファイルの拡張子を「.tsx」に変更し、開発サーバを起動させる

拡張子を変更するファイルは任意で決めて頂いて問題ありません。
私は、参考サイトそのままに「src/index.js」を「src/index.tsx」へ変更しました。

次に、開発サーバーを起動させます。

npm start
# or
yarn start

すると「tsconfig.json」ファイルが生成されます。
f:id:hnm-n-1029:20200914143315p:plain

これで、TypeScriptが導入されました!

tsconfig.jsonについて

TypeScriptで書かれたコードはブラウザでは実行できないため、コンパイルしてJavaScriptに変換する必要があります。
このコンパイルを実行する際のオプションなどを指定するファイルが「tsconfig.json」になります。

tsconfig.jsonの編集については、私自身もまだ分からない部分が多いので今回は割愛します。※勉強してまた記事にしたいと思います。

今回は以上になります!


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp

【JavaScript】複数のモーダルを設置する方法

今回は、自身のポートフォリオで実装している「複数のモーダルを設置する方法」を解説していきます。

モーダル一つだけというのはよく見かけますが、複数の場合の記事が少なかったので初心者の方でも実装できるように分かりやすく解説していきたいと思います。

また、今回はjQueryは使用しません。
なぜなら、私がJavaScriptの理解を深めたいからです笑
jQueryはとても便利なのですが、やはりベースとなるJavaScriptの理解は重要なので今回はvanilla JSで実装していきます。

処理の流れ・考え方

モーダル表示・非表示の基本的な考えは「classの切り替え」です。

簡単に流れをまとめると....

①ページ読み込み時はモーダル自体が非表示になっている。
②クリックイベントで表示用のクラスが付与されモーダルが画面に現れる。
③「✖」をクリックするとクラスが外され非表示になる

この「クラスの切り替え」をJavaScriptが行っています。

複数のモーダルを設置したい場合には、もう一つ大切な考え方があります。
それが、ボタンとモーダルの「紐づけ」です。

複数モーダルの場合は、モーダルのボタンと表示させるモーダルの紐づけが必要

この紐づけを行わないと、ブラウザがどの要素にclassを付与すればいいのか判断できません。
そこで、「data属性」と呼ばれるカスタムデータ属性を使用します。

data属性とは
グローバル属性 - HTML: HyperText Markup Language | MDN

data属性による紐づけの方法

①モーダルのボタンとモーダルにdata属性を持たせる。
②モーダルのボタンをクリックした時にdata属性の値を取得し、同じdata属性の値を持つモーダル要素を探す。
③data属性の値が一致したモーダルの要素に「is_open」というclassを付与し、モーダルを表示させる。

①について、今回は次のように設定しました。
 モーダルのボタン:data-modal-open="任意の値"
 モーダル:data-modal="任意の値"
 そして、この「任意の値」を同じ値に設定して紐づけます。
 (例. modal-1、modal-2、modal-3)

実際のコード:JavaScriptの解説

実際のコードがこちらです。
今回はHTML/CSSの解説は省きます。


See the Pen
yLOXqwP
by naomi homma (@naomi-homma)
on CodePen.

//modalを出す
const modalWrapOpen = function(e) {
  //クリックしたボタンのdata属性の値を取得
  const dataModalOpen = e.currentTarget.dataset.modalOpen;
  //works_modal_wrapper classが付与されている要素を全て取得
  //取得した全ての要素からdata属性の値が同じ要素を探す
  //取得した要素へis_open classを付与する
  Array.from(document.querySelectorAll('.works_modal_wrapper')).forEach((e, i) => {
    if(e.getAttribute('data-modal') === dataModalOpen){
      e.classList.toggle('is_open');
    }
  })
}

//クリックイベントの定義:複数なのでforEachでイテレートさせる
//works_modal_open classが付与された要素を全て取得
//全ての要素に対してクリックイベントを定義する
Array.from(document.querySelectorAll('.works_modal_open')).forEach((modalOpenElement) => {
  modalOpenElement.addEventListener('click', modalWrapOpen);
})

// modalを消す
const modalCloseAction = function(e) {
  //✖が押された要素の親要素(works_modal_wrapper classを持つ)を取得
  //その要素に対してis_openクラスの切り替えを行う
  const targetModal = e.currentTarget.closest('.works_modal_wrapper');
  targetModal.classList.toggle('is_open')
};

//全ての要素に対してクリックイベントを定義する
Array.from(document.querySelectorAll('.works_modal_close')).forEach((modalCloseElement) => {
  modalCloseElement.addEventListener('click', modalCloseAction)
})

今回は以上になります!

誤りやアドバイスなどありましたらコメント頂けますと嬉しいです。


↓↓私の師匠、もりけんさんの武骨日記。問題集、要チェック

kenjimorita.jp