icon

JavaScriptでWeb制作でよく使用するパーツを実装してみた

Web制作でよく使う実装をjavascriptで作ってみた
投稿日時

更新日時

今回は、Web制作でよく使用するパーツをjQueryを使わずに、JavaScriptで作ってみました。

参考サイト↓

しょーごログ(note)

ハンバーガーメニュー

デモサイトはこちら

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Hamburger-js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <header class="header">
    <button type="button" id="jsHam" class="button hamburger__btn" aria-controls="js-global-menu" aria-expanded="false" aria-label="メニューを開閉する">
        <span class="hamburger__line"></span>
    </button>
    <div class="header__sp__menu" id="js-global-menu" aria-hidden="true">
      <nav>
        <ul>
          <li><a href="#">menu01</a></li>
          <li><a href="#">menu02</a></li>
          <li><a href="#">menu03</a></li>
          <li><a href="#">menu04</a></li>
          <li><a href="#">menu05</a></li>
        </ul>
      </nav>
    </div>
  </header>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**scss**/
.button {
  display: inline-block;
  text-decoration: none;
  appearance: none;
  border: none;
  background-color: transparent;
  cursor: pointer;
}

.header {
  width: 100%;
  height: 64px;
  background-color: $color-bg;
  .hamburger__btn {
    position: fixed;
    top: 8px;
    right: 20px;
    z-index: 3;
    width: 48px;
    height: 48px;
    .hamburger__line {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      margin: auto;
      width: 40px;
      height: 1px;
      background-color: $color-main;
      &::before,
      &::after {
        content: "";
        display: block;
        width: 100%;
        height: 100%;
        background-color: inherit;
        position: absolute;
        transition: transform .2s ease-in-out;
      }
      &::before {
        top: -8px;
      }
      &::after {
        bottom: -8px;
      }
    }
  }
  .hamburger__btn[aria-expanded="true"] {
    .hamburger__line {
      background-color: transparent;
      &::before,
      &::after {
        top: 0;
        background-color: $color-main;
        transition: transform .3s ease-in-out;
      }
      &::before {
        transform: rotate(45deg);
      }
      &::after {
        transform: rotate(-45deg);
      }
    }
  }
  &__sp__menu {
    position: fixed;
    width: 100%;
    height: 100vh;
    top: 0;
    left: 0;
    background-color: $color-accent;
    visibility: hidden;
    &.visible {
      visibility: visible;
      animation: fade-in .3s ease-in-out;
    }
  }
}

@keyframes fade-in {
  0% {
    visibility: hidden;
    opacity: 0;
  }
  50% {
    visibility: visible;
    opacity: 0.5;
  }
  100% {
    visibility: visible;
    opacity: 1;
  }
}

JavaScript

/**javascript**/

const jsHamburger = document.getElementById('jsHam');
const body = document.body;
const spHeaderMenu = document.getElementById('js-global-menu');

jsHamburger.addEventListener('click', function(){
  body.classList.toggle('js__drawerActive')
  if(this.getAttribute('aria-expanded') == 'false') {
    this.setAttribute('aria-expanded', 'true');
    spHeaderMenu.classList.add('visible');
    spHeaderMenu.getAttribute('aria-hidden', 'false');
  } else {
    this.setAttribute('aria-expanded', 'false');
    spHeaderMenu.classList.remove('visible');
    spHeaderMenu.getAttribute('aria-hidden', 'true');
  }
});

ドロワーメニュー

デモサイトはこちら

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ドロワーメニュー|Javascript</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <header class="header">
    <button type="button" id="jsHam" class="button hamburger__btn" aria-controls="js-global-menu" aria-expanded="false" aria-label="メニューを開閉する">
      <span class="hamburger__line"></span>
    </button>
    <div class="header__sp__menu" id="js-global-menu" area-hidden="true">
      <nav>
        <ul>
          <li><a href="">menu01</a></li>
          <li><a href="">menu02</a></li>
          <li><a href="">menu03</a></li>
          <li><a href="">menu04</a></li>
          <li><a href="">menu05</a></li>
        </ul>
      </nav>
    </div>
    <div class="drawer__bg" id="js-drawer-bg"></div>
  </header>


  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**scss**/

@use '../setting/setting' as *;

.header {
  background-color: $color-main;
  height: 64px;
  .button {
    display: inline-block;
    text-decoration: none;
    border: none;
    cursor: pointer;
    background-color: transparent;
  }
  .hamburger__btn {
    position: fixed;
    top: 8px;
    right: 24px;
    z-index: 5;
    width: 48px;
    height: 48px;
    background-color: transparent;
    .hamburger__line {
      position: absolute;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
      margin: auto;
      width: 80%;
      height: 2px;
      background-color: $color-black;
      &::before,
      &::after {
        content: "";
        background-color: inherit;
        position: absolute;
        display: block;
        width: 100%;
        height: 100%;
        transition: transform .3s cubic-bezier(0,.26,.69,1);
      }
      &::before {
        top: -8px;
      }
      &::after {
        bottom: -8px;
      }
    }
  }
  &__sp__menu {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 4;
    width: 280px;
    height: 100vh;
    background-color: $color-black;
    transform: translateX(280px);
    overflow: hidden;
    transition: transform .3s cubic-bezier(0,.26,.69,1);
    a {
      color: $color-white;
    }
  }
  .drawer__bg {
    position: fixed;
    z-index: 3;
    width: 100%;
    height: 100vh;
    top: 0;
    right: 0;
    left: 0;
    bottom: 0;
    margin: auto;
    background-color: $color-black;
    visibility: hidden;
    opacity: 0;
  }
}

.hamburger__btn[aria-expanded="true"] {
  .hamburger__line {
    background-color: transparent;
    &::before,
    &::after {
      top: 0;
      background-color: $color-white;
    }
    &::before {
      transform: rotate(45deg);
    }
    &::after {
      transform: rotate(-45deg);
    }
  }
}

.is-drawerActive {
  .header__sp__menu {
    transform: translateX(0);
    transition: transform .4s cubic-bezier(.57,.03,.6,.7);
  }
  .drawer__bg {
    visibility: visible;
    opacity: 0.7;
    transition: .3s;
  }
}

JavaScript

/**js**/

const jsHamburger = document.getElementById('jsHam');
const body = document.body;
const spHeaderMenu = document.getElementById('js-global-menu');
const drawerBg = document.getElementById('js-drawer-bg');

jsHamburger.addEventListener('click', function(){
  body.classList.toggle('is-drawerActive')
  if(this.getAttribute('aria-expanded') == 'false') {
    this.setAttribute('aria-expanded', 'true');
    spHeaderMenu.setAttribute('area-hidden','false')
  } else {
    this.setAttribute('aria-expanded', 'false');
    spHeaderMenu.setAttribute('area-hidden', 'true')
  };
});

drawerBg.addEventListener('click', () => {
    body.classList.remove('is-drawerActive')
    jsHamburger.setAttribute('aria-expanded', 'false')
    spHeaderMenu.setAttribute('area-hidden', 'true')
});

ローディング

デモサイトはこちらから

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ローディング|js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <div id="loadingBox">
    <div class="spinner-box">
      <div class="pulse-container">  
        <div class="pulse-bubble pulse-bubble-1"></div>
        <div class="pulse-bubble pulse-bubble-2"></div>
        <div class="pulse-bubble pulse-bubble-3"></div>
      </div>
    </div>
  </div>
  <section class="contents">
    <h1>main_content</h1>
  </section>
  
  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**scss**/

.contents {
  width: 100%;
  height: 100vh;
  background-color: $color-main;
  text-align: center;
  h1 {
    color: $color-black;
    font-size: 2.4rem;
    padding-top: 46vh;
  }
}

#loadingBox {
  width: 100%;
  height: 100vh;
  background-color: $loading-bg-color;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  .spinner-box {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    .pulse-container {
      width: 120px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      .pulse-bubble {
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background-color: $loading-color;
        &-1 {
          animation: pulse .4s ease .0s infinite alternate;
        }
        &-2 {
          animation: pulse .4s ease .2s infinite alternate;
        }
        &-3 {
          animation: pulse .4s ease .4s infinite alternate;
        }
      }
    }
  }
}

.loadingNone {
  animation: loadingAnime 1s  2s forwards;
}

@keyframes pulse {
  from {
    opacity: 1;
    transform: scale(1);
  }
  to {
    opacity: .25;
    transform: scale(.75);
  }
}

@keyframes loadingAnime {
  0% {
    opacity: 1;
  }
  99% {
    opacity: 0;
  }
  100% {
    opacity: 0;
    display: none;
  }
}

JavaScript

/**javascript**/
const loadingBox = document.getElementById('loadingBox');

function loadingStop() {
  loadingBox.classList.add('loadingNone');
}
window.addEventListener('load', loadingStop());

ページトップ

デモサイトはこちらから

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ページトップ|js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <h1>page-top</h1>
  <div id="js-scroll-fadein" class="js-scroll-fadein arrow"></div>
  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**Scss**/

h1 {
  font-size: 3rem;
  text-align: center;
  color: $color-black;
}

.js-scroll-fadein {
  transition: opacity 1s;
  visibility: hidden;
  opacity: 0;
  &.js-fadein {
    visibility: visible;
    opacity: 1;
  }
}
.arrow {
  position: fixed;
  right: 16px;
  bottom: 24px;
  cursor: pointer;
  background-color: $color-bg;
  box-shadow: 8px 8px 20px rgb(112 112 112 / 20%), -8px -8px 20px #fff;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  &:hover {
    box-shadow: 8px 8px 20px rgb(160 160 160 / 20%), -8px -8px 20px #fff, inset 4px 4px 10px rgb(160 160 160 / 20%), inset -4px -4px 10px #fff;
  }
  &::before {
    content: "";
    width: 24px;
    height: 24px;
    border-top: solid 3px $color-black;
    border-right: solid 3px $color-black;
    transform: rotate(-45deg);
    position: absolute;
    top: 10px;
    left: 0;
    right: 0;
    bottom: 0;
    margin: auto;
  }
}

JavaScript

/**javascript**/

const topButton = document.getElementById('js-scroll-fadein'); 

function getScrolled() {
  return( window.pageYOffset !== undefined) ? window.pageYOffset: document.documentElement.scrollTop;
}

window.onscroll = function() {
  (getScrolled() > 500) ? topButton.classList.add('js-fadein'): topButton.classList.remove('js-fadein');
}

function scrollToTop(){
  window.scrollTo({
    top: 0,
    behavior: 'smooth'
  });
}

topButton.onclick = function(){
  scrollToTop();
}

モーダルウィンドウ

デモサイトはこちらから

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>モーダルウィンドウ|js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <button id="openModal">OPEN MODAL</button>

  <section id="modalArea" class="modalArea">
    <div id="modalBg" class="modalBg"></div>
    <div class="modalWrapper">
      <div class="modalContents">
        <p>テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□テキスト□</p>
      </div>
      <div id="closeModal" class="closeModal">×</div>
    </div>
  </section>

  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**scss**/

.modalArea {
  visibility: hidden;
  opacity: 0;
  position: fixed;
  z-index: 10;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  transition: .4s;
  .modalBg {
    width: 100%;
    height: 100%;
    background-color: $color-modal-bg;
  }
  .modalWrapper {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    width: 85%;
    max-width: 800px;
    padding: 45px;
    background-color: $color-black;
    .modalContents {
      p {
        font-size: 16px;
        color: $color-white;
      }
    }
    .closeModal {
      position: absolute;
      top: -11px;
      right: 16px;
      font-size: 3rem;
      color: $color-white;
      cursor: pointer;
    }
  }
  &.is-show {
    visibility: visible;
    opacity: 1;
  }
}

#openModal {
  width: 250px;
  height: 60px;
  font-size: 1.6rem;
  text-align: center;
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  border: 2px solid $color-black;
  background-color: $color-white;
  color: $color-black;
  &:hover {
    background-color: $color-black;
    color: $color-white;
  }
}

JavaScript

/**javascript**/

const modalArea = document.getElementById('modalArea');
const openModal = document.getElementById('openModal');
const closeModal = document.getElementById('closeModal');
const modalBg = document.getElementById('modalBg');
const toggle = [openModal,closeModal,modalBg];

for(let i = 0; i < toggle.length; i++) {
  toggle[i].addEventListener('click', function(){
    modalArea.classList.toggle('is-show');
  },false);
}

アコーディオン

デモサイトはこちらから

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>アコーディオン|js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <section class="accordion">
    <div class="accordion__container">
      <h4 class="accordion__ttl jsAccordionTitle">アコーディオン01</h4>
      <div class="accordion__content">アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容</div>
      <h4 class="accordion__ttl jsAccordionTitle">アコーディオン02</h4>
      <div class="accordion__content">アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容</div>
      <h4 class="accordion__ttl jsAccordionTitle">アコーディオン03</h4>
      <div class="accordion__content">アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容</div>
      <h4 class="accordion__ttl jsAccordionTitle">アコーディオン04</h4>
      <div class="accordion__content">アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容アコーディオンの内容</div>
    </div>
  </section>

  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**Scss**/
$color-black: #000;
$color-gray: rgb(255, 237, 98);
$color-white: #fff;
$color-active: rgb(227, 227, 227);

.accordion {
  margin-top: 42px;
  &__container {
    width: 72%;
    margin: 0 auto;
  }
  &__ttl {
    background-color: $color-white;
    border-top: 1px solid $color-black;
    color: $color-black;
    font-size: 18px;
    padding: 12px 12px 12px 32px;
    cursor: pointer;
    position: relative;
    &:last-of-type {
      border-bottom: 1px solid $color-black;
    }
    &::before,
    &::after {
      content: "";
      display: block;
      background-color: $color-black;
      position: absolute;
      top: 50%;
      width: 32px;
      height: 2px;
      right: 24px;
      transform: translateY(-50%);
    }
    &::after {
      transform: rotate(90deg);
      transition-duration: .3s;
    }
    &:hover,
    &:active,
    &.is-active {
      background-color: $color-active;
    }
    &.is-active {
      &::before {
        opacity: 0;
      }
      &::after {
        transform: rotate(0);
      }
    }
  }
  &__content {
    padding: 0 16px;
    height: 0;
    opacity: 0;
    line-height: 0;
    overflow: hidden;
    transition-duration: .3s;
    &.is-open {
      background-color: $color-gray;
      line-height: normal;
      margin: 12px 0;
      padding: 16px;
      height: auto;
      opacity: 1;
    }
  }
}

JavaScript

/**javascript**/

const ttl = document.querySelectorAll('.jsAccordionTitle');

ttl.forEach(ttlEach => {
    let content = ttlEach.nextElementSibling;
    ttlEach.addEventListener('click', () => {
      ttlEach.classList.toggle('is-active');
      content.classList.toggle('is-open');
    })
});

タブ

デモサイトはこちらから

HTML

/**html**/

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>タブ|js</title>
  <link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css">
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <section class="tab">
    <ul class="tab__menu">
      <li class="tab__menu__item">
        <span class="tab__trigger js__tab__trigger is-active" data-id="tab-01">タブ01</span>
      </li>
      <li class="tab__menu__item">
        <span class="tab__trigger js__tab__trigger" data-id="tab-02">タブ02</span>
      </li>
      <li class="tab__menu__item">
        <span class="tab__trigger js__tab__trigger" data-id="tab-03">タブ03</span>
      </li>
    </ul>
    <div class="tab__content">
      <div class="tab__content__item js__tab__target is-active" id="tab-01">
        <p>テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01テキスト01</p>
      </div>
      <div class="tab__content__item js__tab__target" id="tab-02">
        <p>テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02テキスト02</p>
      </div>
      <div class="tab__content__item js__tab__target" id="tab-03">
        <p>テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03テキスト03</p>
      </div>
    </div>
  </section>

  <script type="text/javascript" src="js/main.js"></script>
</body>
</html>

Scss

/**scss**/

$color-black: #000;
$color-white: #fff;
$color-accent: #c0e4ff;
$color-border: rgb(222, 222, 222);
$color-bg: #efefef;

a {
  list-style: none;
  text-decoration: none;
}

li {
  list-style: none;
}

.tab {
  width: 72%;
  margin: 50px auto;
  &__menu {
    display: flex;
    &__item {
      &:nth-child(even) {
        margin: 0 20px;
      }
    }
  }
  &__trigger {
    cursor: pointer;
    display: block;
    padding: 8px 32px;
    border: 1px solid $color-border;
    border-bottom: 0;
    border-radius: 3px 3px 0 0;
    overflow: hidden;
    background-color: $color-bg;
  }
  &__trigger.is-active {
    background-color: $color-accent;
  }
  &__content {
    border: 1px solid $color-border;
    border-radius: 0 3px 3px 3px;
    &__item {
      padding: 24px;
      box-sizing: border-box;
      display: none;
    }
    &__item.is-active {
      display: block;
      animation: fade .5s ease-in-out;
    }
  }
}

@keyframes fade {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

JavaScript

/**javascript**/
document.addEventListener('DOMContentLoaded', () => {

  const tabTrigger = document.querySelectorAll('.js__tab__trigger');
  const tabTarget = document.querySelectorAll ('.js__tab__target');

  tabTrigger.forEach(tabTriggers => {
    tabTriggers.addEventListener('click', (e) => {
      let currentMenu = e.currentTarget;
      let currentContent = document.getElementById(currentMenu.dataset.id);
      tabTrigger.forEach(tabTriggers => {
        tabTriggers.classList.remove('is-active');
      });

      currentMenu.classList.add('is-active');

      tabTarget.forEach(tabTargets => {
        tabTargets.classList.remove('is-active');
      });
      if (currentContent !== null) {
        currentContent.classList.add('is-active');
      }
    }); 
  });
});