🐉

汎用JavaScriptのまとめ

2023.04.17

SHARE

TABLE OF CONTENTS

    🐉

    汎用JavaScriptのまとめ

    2023.04.17

    筆者自身が汎用的に使用する以下のJavaScript(VanillaJs)をまとめました。
    今後も日々アップデートしていきます。

    スムーススクロール(アンカーリンク)

    CodePen

    See the Pen Smooth scroll to Anker link by ShoyaKajita (@ShoyaKajita) on CodePen.

    Code

    export class AnkerLink {
      constructor() {
        this.idName = {
          header: "header",
          scrollTop: "scrollTop",
        };
    
        this.distanceFromTop = 0;
      }
    
      /**
       * スムーススクロール
       * @param {number} y
       * @param {number} x
       */
      scrollTo(y = 0, x = 0) {
        window.scrollTo({
          top: y,
          left: x,
          behavior: "smooth", // ページ内ではスムーススクロールを設定
        });
      }
    
      /**
       * 移動先の座標を取得しスムーススクロールをする
       * @param {event} e // クリックイベント
       */
      setScrollPosition(e) {
        e.preventDefault();
        const href = e.target.getAttribute("href"),
          string = href.slice(0, 1),
          header = document.getElementById(this.idName.header),
          offset = header
            ? header.getBoundingClientRect().height + this.distanceFromTop
            : this.distanceFromTop;
    
        if (string === "#" && (href != "" || href != "#")) {
          const target = document.querySelector(href);
          if (target) {
            const y =
              target.getBoundingClientRect().top - offset + window.pageYOffset;
            this.scrollTo(y, 0);
          }
        }
      }
    
      /**
       * 初期位置の座標を取得し移動する
       */
      setInitPosition() {
        let id = null,
          y = 0;
        const delay = 200,
          hash = window.location.hash,
          query = this.getParameter("id"),
          header = document.getElementById(this.idName.header),
          offset = header
            ? header.getBoundingClientRect().height + this.distanceFromTop
            : this.distanceFromTop;
    
        if (hash != "") {
          id = document.querySelector(hash);
          if (id != null) {
            y = id.getBoundingClientRect().top - offset + window.pageYOffset;
            setTimeout(() => {
              window.scrollTo(0, y);
            }, delay);
          }
        } else if (query != "" && query != null) {
          id = document.getElementById(`${query}`);
          if (id != null) {
            y = id.getBoundingClientRect().top - offset + window.pageYOffset;
            setTimeout(() => {
              window.scrollTo(0, y);
            }, delay);
          }
        }
      }
    
      /**
       * GETパラメータのキーから値を取得し返す
       * @param {string} name // 取得したいGETパラメータのキー
       * @returns GETパラメータ値
       */
      getParameter(name) {
        // https://www-creators.com/archives/4463
        name = name.replace(/[\[\]]/g, "\\$&");
        let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
          results = regex.exec(window.location.href);
        if (!results) return null;
        if (!results[2]) return "";
        return decodeURIComponent(results[2].replace(/\+/g, " "));
      }
    
      init() {
        this.setInitPosition();
    
        const linkList = [...document.querySelectorAll("a[href^='#']")];
        if (linkList) {
          linkList.forEach((ele) => {
            ele.addEventListener("click", this.setScrollPosition.bind(this));
          });
        }
    
        const scrollTop = document.getElementById(this.idName.scrollTop);
        if (scrollTop) {
          scrollTop.addEventListener("click", (e) => {
            this.scrollTo(0, 0);
          });
        }
      }
    }

    IntersectionObserver

    /*
    // --------------------------
    // html
    // --------------------------
    // 監視要素 | 「data-delay」が最優先される
    <div class="js-observer" data-delay="0" data-delay-pc="0" data-delay-sp="0"></div>
    
    // --------------------------
    // css
    // --------------------------
    .js-observer.is-cue {
      // 発火したスタイルを記述する
    }
    */
    export class Observer {
      constructor() {
        this.isMatchMedia = window.matchMedia("(max-width: 768px)").matches;
        this.config = {
          root: null,
          rootMargin: "0% 0%",
          threshold: 0,
        };
      }
    
      reset() {
        this.targetList = [];
        this.destroy();
        this.observer = null;
      }
    
      /**
       * @param {object} 複数の監視要素の情報が格納されている
       */
      entry(entries) {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            const ele = entry.target;
            const dataDelay = ele.getAttribute("data-delay");
            let delay = dataDelay ? dataDelay : 0;
    
            if (this.isMatchMedia) {
              const dataDelaySp = ele.getAttribute("data-delay-sp");
              if (dataDelaySp) delay = dataDelaySp;
            } else {
              const dataDelayPc = ele.getAttribute("data-delay-pc");
              if (dataDelayPc) delay = dataDelayPc;
            }
    
            setTimeout(() => {
              if (ele) ele.classList.add("is-cue");
            }, delay);
          }
        });
      }
    
      destroy() {
        this.observer.disconnect(); // 監視を終了する
      }
    
      init() {
        this.targetList = [];
        this.observer = null;
    
        this.observer = new IntersectionObserver(
          this.entry.bind(this),
          this.config
        );
        this.targetList = [...document.querySelectorAll(".js-observer")];
        this.targetList.forEach((ele) => {
          this.observer.observe(ele);
        });
      }
    }
    

    セッションストレージ

    export class Session {
      clear() {
        sessionStorage.clear();
      }
    
      /**
       * @param {string} key // 削除するキー名
       */
      remove(key) {
        sessionStorage.removeItem(key);
        console.log("session remove", key);
      }
    
      /**
       * @param {string} key //取得したいキー名
       * @returns {string} // 値
       */
      get(key) {
        console.log("session get", key, sessionStorage.getItem(key));
        return sessionStorage.getItem(key);
      }
    
      /**
       * @param {string} key // セットしたいキー名
       * @param {string} value // セットしたい値
       */
      set(key, value) {
        sessionStorage.setItem(key, value);
        console.log("session set", key, value, sessionStorage.getItem(key));
      }
    }
    

    ユーザーエージェント

    現在(2023.01.01)「M1、M2」と「Macbook Pro Air」とのユーザーエージェントの違いを確認できていません...

    export class Ua {
      constructor(body) {
        this.body = body;
        this.bodyClassList = this.body.classList;
        this.ua = window.navigator.userAgent.toLowerCase();
        this.data = {
          browser: "",
          os: "",
          device: "",
          iphone: "",
        };
    
        this.checkBrowser();
        this.checkOs();
        this.checkDevice();
        this.checkiPhone();
      }
    
      checkBrowser() {
        if (
          this.ua.indexOf("edge") !== -1 ||
          this.ua.indexOf("edga") !== -1 ||
          this.ua.indexOf("edgios") !== -1
        ) {
          console.log("🚀 ~ This is Edge");
          this.data.browser = "edge";
        } else if (
          this.ua.indexOf("opera") !== -1 ||
          this.ua.indexOf("opr") !== -1
        ) {
          console.log("🚀 ~ This is Opera");
          this.data.browser = "opera";
        } else if (this.ua.indexOf("samsungbrowser") !== -1) {
          console.log("🚀 ~ This is SamsungInternetBrowser");
          this.data.browser = "samsung";
        } else if (this.ua.indexOf("ucbrowser") !== -1) {
          console.log("🚀 ~ This is UcBrowser");
          this.data.browser = "uc";
        } else if (
          this.ua.indexOf("chrome") !== -1 ||
          this.ua.indexOf("crios") !== -1
        ) {
          console.log("🚀 ~ This is Chrome");
          this.data.browser = "chrome";
        } else if (
          this.ua.indexOf("firefox") !== -1 ||
          this.ua.indexOf("fxios") !== -1
        ) {
          console.log("🚀 ~ This is Firefox");
          this.data.browser = "firefox";
        } else if (this.ua.indexOf("safari") !== -1) {
          console.log("🚀 ~ This is Safari");
          this.data.browser = "safari";
        } else if (
          this.ua.indexOf("msie") !== -1 ||
          this.ua.indexOf("trident") !== -1
        ) {
          console.log("🚀 ~ This is IE");
          this.data.browser = "ie";
          alert("このブラウザは現在サポートされておりません。");
        } else {
          console.log("🚀 ~ This is An unknown browser");
          this.data.browser = "";
        }
    
        if (this.data.browser != "") this.bodyClassList.add(this.data.browser);
      }
    
      checkOs() {
        if (this.ua.indexOf("windows nt") !== -1) {
          console.log("🚀 ~ This is Windows");
          this.data.os = "windows";
        } else if (this.ua.indexOf("android") !== -1) {
          console.log("🚀 ~ This is Android");
          this.data.os = "android";
        } else if (
          this.ua.indexOf("iphone") !== -1 ||
          this.ua.indexOf("ipad") !== -1
        ) {
          console.log("🚀 ~ This is iOS");
          this.data.os = "ios";
        } else if (this.ua.indexOf("mac os x") !== -1) {
          console.log("🚀 ~ This is macOS");
          this.data.os = "macos";
        } else {
          console.log("🚀 ~ This is An unknown OS");
          this.data.os = "";
        }
    
        if (this.data.os != "") this.bodyClassList.add(this.data.os);
      }
    
      checkDevice() {
        if (
          this.ua.indexOf("iphone") !== -1 ||
          (this.ua.indexOf("android") !== -1 && this.ua.indexOf("Mobile") > 0)
        ) {
          console.log("🚀 ~ This is Mobile");
          this.data.device = "mobile";
        } else if (
          this.ua.indexOf("ipad") !== -1 ||
          this.ua.indexOf("android") !== -1
        ) {
          console.log("🚀 ~ This is Tablet");
          this.data.device = "tablet";
        } else if (
          this.ua.indexOf("ipad") > -1 ||
          (this.ua.indexOf("macintosh") > -1 && "ontouchend" in document)
        ) {
          console.log("🚀 ~ This is iPad");
          this.data.device = "tablet";
        } else {
          console.log("🚀 ~ This is PC");
          this.data.device = "pc";
        }
    
        if (this.data.device != "") this.bodyClassList.add(this.data.device);
      }
    
      checkiPhone() {
        if (this.ua.indexOf("iphone") !== -1) {
          console.log("🚀 ~ This is iPhone");
          this.data.iphone = "iphone";
        } else {
          this.data.iphone = "";
        }
    
        if (this.data.iphone != "") this.bodyClassList.add(this.data.iphone);
      }
    }

    1文字分割

    /*
    // --------------------------
    // html
    // --------------------------
    // before
    <div class="js-splitText">これは文字列です。</div>
    
    // after
    <div class="js-splitText">
      <span class="t" style="display: inline-block;">こ</span><span class="t" style="display: inline-block;">れ</span><span class="t" style="display: inline-block;">は</span><span class="t" style="display: inline-block;">文</span><span class="t" style="display: inline-block;">字</span><span class="t" style="display: inline-block;">列</span><span class="t" style="display: inline-block;">で</span><span class="t" style="display: inline-block;">す</span><span class="t" style="display: inline-block;">。</span>
    </div>
    
    // --------------------------
    // scss
    // --------------------------
    // 遅延設定(1文字アニメーション用)
    .t{
      @for $i from 1 through 100 {
        &:nth-of-type(#{$i}) {
          transition-delay: (0.05s * $i) - 0.05s;
        }
      }
    }
    */
    export class SplitText {
      /**
       * 分割した1文字を要素にする
       * @param {string} text 1文字
       * @returns {element} 要素 | <span class="t" style="display: inline-block;">1文字</span>
       */
      createEle(text) {
        const ele = document.createElement("span");
        ele.className = "t";
        ele.setAttribute("style", "display: inline-block;");
        ele.innerHTML = text != " " ? text : "  ;"; // ⚠️ 目視するために半角スペースを入れている
        return ele;
      }
    
      /**
       * 文字列を要素に分割し追加する
       * @param {element} obj.wrap 文字列の格納先
       * @param {string} obj.text 分割する文字列
       */
      createSplit(obj) {
        const wrap = obj.wrap,
          text = obj.text;
    
        const array = text.split("");
        const fragment = document.createDocumentFragment();
    
        for (let i = 0; i < array.length; i++) {
          const ele = this.createEle(array[i]);
          fragment.appendChild(ele);
        }
    
        wrap.appendChild(fragment);
      }
    
      init() {
        this.eleList = [...document.querySelectorAll(".js-splitText")];
        this.eleList.forEach((ele) => {
          const obj = {
            wrap: ele,
            text: ele.innerText,
          };
          ele.innerText = "";
          if (obj.text != "") this.createSplit(obj);
        });
      }
    }

    メニュー

    /* 
    // --------------------------
    // html
    // --------------------------
    // メニューボタン
    <div id="menuBtn">メニュー</div>
    
    // 閉じるボタン
    <div class="js-menuClose">閉じる</div>
    
    // --------------------------
    // css
    // --------------------------
    body[data-menu="open"] {
      overflow: hidden;
    }
    */
    export class Menu {
      constructor(body = document.body) {
        this.body = body;
        this.body.setAttribute("data-menu", "close");
    
        this.isOpenMenu = false;
        this.isAbleToClick = true;
        this.interval = 600;
    
        this.menuBtn = document.getElementById("menuBtn");
        this.menuBtn.addEventListener("click", (e) => {
          if (this.isAbleToClick) {
            this.isAbleToClick = false;
            this.isOpenMenu ? this.toCloseMenu() : this.toOpenMenu();
            setTimeout(() => {
              this.isAbleToClick = true;
            }, this.interval);
          }
        });
      }
    
      toOpenMenu() {
        this.isOpenMenu = true;
        this.body.setAttribute("data-menu", "open");
      }
    
      toCloseMenu() {
        this.isOpenMenu = false;
        this.body.setAttribute("data-menu", "close");
      }
    
      init() {
        this.isAbleToClick = true;
        this.isOpenMenu = false;
        this.body.setAttribute("data-menu", "close");
      }
    }

    アコーディオン|detailsを使用したもの

    CodePen

    Code

    /*
    // HTML 
    <details class="c-details">
      <summary class="c-details__summary">
        <h2>Summary</h2>
      </summary>
      <div class="c-details__content">
        <div class="c-details__inner">
          コンテンツ // ここで`padding`で脂肪をつける
        </div>
      </div>
    </details>
    
    // css
    summary {
      display: block; // list-item; → 三角形のアイコンを表示する
      cursor: pointer;
    }
    //Safariの三角形アイコンを非表示にする
    summary::-webkit-details-marker {
      display: none;
    }
    */
    export class Accordion {
      constructor() {
        this.isMatchMedia = window.matchMedia("(max-width: 768px)").matches;
        this.config = {
          duration: 300,
          easing: "ease",
        };
      }
    
      reset() {}
    
      resizeAfter() {
        this.detailsList.forEach((ele) => {
          const summary = ele.querySelector("summary"),
            content = summary.nextElementSibling;
          content.style.height = "auto";
        });
      }
    
      /**
       * @param {element} obj.details // コンテナ
       * @param {element} obj.summary // 開閉ボタン
       * @param {element} obj.content // 開閉要素
       */
      open(obj) {
        const details = obj.details,
          summary = obj.summary,
          content = obj.content;
    
        const openAnimeKeyframes = (content) => [
          {
            height: 0,
            opacity: 0,
          },
          {
            height: content.offsetHeight + "px",
            opacity: 1,
          },
        ];
    
        details.setAttribute("open", "true");
        details.setAttribute("data-status", "open");
    
        const openingAnime = content.animate(
          openAnimeKeyframes(content),
          this.config
        );
        openingAnime.onfinish = () => {
          // 終了時の処理
        };
      }
    
      close(obj) {
        const details = obj.details,
          summary = obj.summary,
          content = obj.content;
    
        const closeAnimeKeyframes = (content) => [
          {
            height: content.offsetHeight + "px",
            opacity: 1,
          },
          {
            height: 0,
            opacity: 0,
          },
        ];
    
        const closingAnime = content.animate(
          closeAnimeKeyframes(content),
          this.config
        );
    
        closingAnime.onfinish = () => {
          details.removeAttribute("open");
          details.setAttribute("data-status", "close");
        };
      }
    
      init() {
        this.detailsList = [];
        this.detailsList = [...document.querySelectorAll("details")];
        this.detailsList.forEach((ele) => {
          const isOpen = ele.getAttribute("data-open");
          if (
            (isOpen === "pc" && !this.isMatchMedia) ||
            (isOpen === "sp" && this.isMatchMedia)
          ) {
            ele.setAttribute("open", "true");
            ele.setAttribute("data-status", "open");
          } else {
            ele.removeAttribute("open");
            ele.setAttribute("data-status", "close");
          }
    
          const summary = ele.querySelector("summary"),
            content = summary.nextElementSibling;
    
          content.setAttribute(
            "style",
            `overflow: hidden; transition: ${
              this.config.duration / 1000
            }s ease height;`
          );
    
          const obj = {
            details: ele,
            summary: summary,
            content: content,
          };
    
          summary.addEventListener("click", (e) => {
            e.preventDefault();
            if (
              ele.getAttribute("data-status") === "open" ||
              ele.getAttribute("data-status") === "close"
            ) {
              ele.setAttribute("data-status", "run");
              ele.open ? this.close(obj) : this.open(obj);
            }
          });
        });
      }
    }
    

    モーダル|ID指定(複数設置)

    CodePen

    Code

    /*
    // --------------------------
    // html
    // --------------------------
    // モーダルボタン
    <button class="js-modalBtn" data-modal-id="modal1">モーダルボタン</button>
    
    // モーダル要素
    <div class="l-modal" id="modal1">
      <div class="js-modalCloseBtn" data-modal-id="modal1">閉じる</div>
      <div class="js-modalPrevBtn" data-modal-id="modal3">前のモーダルへ</div>
      <div class="js-modalNextBtn" data-modal-id="modal2">▶次のモーダルへ</div>
    </div>
    
    // --------------------------
    // css
    // --------------------------
    body.is-openModalId{
      overflow: hidden;
    }
    .l-modal.is-openModalId {
      // モーダルが開く
    }
    .js-modalCloseBtn,
    .js-modalPrevBtn,
    .js-modalNextBtn{
      cursor: pointer;
    }
    */
    export class ModalId {
      constructor(body = document.body) {
        this.body = body;
        this.bodyClassList = body.classList;
    
        this.timer = {
          modal: null,
        };
    
        this.className = {
          open: "is-openModalId",
        };
    
        this.isOpenModal = false;
        this.isAbleToClick = true;
        this.interval = 500;
        this.currentId = "";
      }
    
      removeClassName() {
        if (this.bodyClassList.contains(this.className.open)) this.bodyClassList.remove(this.className.open);
      }
    
      addClassName() {
        this.bodyClassList.add(this.className.open);
      }
    
      reset() {
        this.isOpenModal = false;
        this.isAbleToClick = false;
        clearTimeout(this.timer.modal);
        this.currentId = "";
      }
    
      toCloseModal(target) {
        console.log("open");
        this.isOpenModal = false;
        this.removeClassName();
        if (target.classList.contains(this.className.open)) target.classList.remove(this.className.open);
      }
    
      toOpenModal(target) {
        console.log("close");
        this.isOpenModal = true;
        this.addClassName();
        target.classList.add(this.className.open);
      }
    
      toClick(ele) {
        console.log("isClick");
        if (this.isAbleToClick) {
          this.isAbleToClick = false;
    
          const targetId = ele.getAttribute("data-modal-id");
          console.log(targetId);
          if (targetId) {
            this.currentId = targetId;
            const target = document.getElementById(targetId);
            target.scrollTo(0, 0);
            console.log(target);
            if (this.isOpenModal) {
              this.currentId = "";
              this.toCloseModal(target);
            } else {
              this.currentId = targetId;
              this.toOpenModal(target);
            }
    
            clearTimeout(this.timer.modal);
            this.timer.modal = setTimeout(() => {
              this.isAbleToClick = true;
            }, this.interval);
          } else {
            this.isAbleToClick = true;
            this.isOpenModal = false;
          }
        }
      }
    
      toChange(ele) {
        if (this.isAbleToClick) {
          this.isAbleToClick = false;
          const currentTarget = document.getElementById(this.currentId);
          if (currentTarget) {
            this.currentId = "";
            this.toCloseModal(currentTarget);
          }
    
          const targetId = ele.getAttribute("data-modal-id");
          if (targetId) {
            this.currentId = targetId;
            const target = document.getElementById(targetId);
            target.scrollTo(0, 0);
            if (this.isOpenModal) {
              this.currentId = "";
              this.toCloseModal(target);
            } else {
              this.currentId = targetId;
              this.toOpenModal(target);
            }
    
            clearTimeout(this.timer.modal);
            this.timer.modal = setTimeout(() => {
              this.isAbleToClick = true;
            }, this.interval);
          } else {
            this.isAbleToClick = true;
            this.isOpenModal = false;
          }
        }
      }
    
      init() {
        console.log("🚀 ~ Modal init");
        this.isOpenModal = false;
        this.isAbleToClick = true;
    
        // 開くボタン
        this.modalBtnList = [...document.querySelectorAll(".js-modalBtn")];
        if (this.modalBtnList) {
          this.modalBtnList.forEach((ele) => {
            ele.addEventListener("click", (e) => {
              this.toClick(ele);
            });
          });
        }
    
        // 閉じるボタン
        this.closeBtnList = [...document.querySelectorAll(".js-modalCloseBtn")];
        if (this.closeBtnList) {
          this.closeBtnList.forEach((ele) => {
            ele.addEventListener("click", (e) => {
              this.toClick(ele);
            });
          });
        }
    
        // prevボタン
        this.prevBtnList = [...document.querySelectorAll(".js-modalPrevBtn")];
        if (this.prevBtnList) {
          this.prevBtnList.forEach((ele) => {
            ele.addEventListener("click", (e) => {
              this.toChange(ele);
            });
          });
        }
    
        // nextボタン
        this.nextBtnList = [...document.querySelectorAll(".js-modalNextBtn")];
        if (this.nextBtnList) {
          this.nextBtnList.forEach((ele) => {
            ele.addEventListener("click", (e) => {
              this.toChange(ele);
            });
          });
        }
      }
    }

    モーダル|画像

    CodePen

    Code

    /*
    // --------------------------
    // html
    // --------------------------
    // ボタン
    <div class="js-modalImgBtn">
      <img src="#" alt="" />
    </div>
    
    // モーダル
    <div class="l-modalImg">
      <div class="js-modalImgCloseBtn"></div>
      <div id="modalImgTarget">
        <!-- ここにimgタグがJSの処理によって入ります。 -->
      </div>
    </div>
    
    // --------------------------
    // css
    // --------------------------
    body.is-openModalImg {
      overflow: hidden;
    }
    .js-modalImgBtn,
    .js-modalImgCloseBtn {
      cursor: pointer;
    }
    */
    export class ModalImg {
      constructor(body = document.body) {
        this.body = body;
        this.bodyClassList = this.body.classList;
    
        this.className = {
          open: "is-openModalImg",
        };
    
        this.isPageEnter = true;
        this.isAbleToClick = true;
        this.interval = 200;
      }
    
      reset() {
        this.isPageEnter = false;
        this.isAbleToClick = false;
      }
    
      removeClassName() {
        if (this.bodyClassList.contains(this.className.open))
          this.bodyClassList.remove(this.className.open);
      }
    
      addClassName() {
        this.bodyClassList.add(this.className.open);
      }
    
      toCloseModal() {
        if (this.isPageEnter) {
          this.isAbleToClick = false;
          this.removeClassName();
          setTimeout(() => {
            if (this.isPageEnter) this.isAbleToClick = true;
          }, this.interval);
        }
      }
    
      /**
       * @param {event} e // クリックイベント
       */
      toOpenModal(e) {
        if (this.isPageEnter) {
          this.isAbleToClick = false;
    
          const ele = e.target;
          const src = ele.querySelector("img").getAttribute("src");
          const img = `<img src="${src}" alt="">`;
    
          this.target.innerHTML = img;
    
          this.addClassName();
          setTimeout(() => {
            if (this.isPageEnter) this.isAbleToClick = true;
          }, this.interval);
        }
      }
    
      init() {
        console.log("🚀 ~ Modal Image");
    
        this.isPageEnter = true;
        this.isAbleToClick = true;
    
        this.btnList = [...document.querySelectorAll(".js-modalImgBtn")];
        this.closeList = [...document.querySelectorAll(".js-modalImgCloseBtn")];
        this.target = document.getElementById("modalImgTarget");
    
        // 開くボタン
        this.btnList.forEach((ele) => {
          ele.addEventListener("click", (e) => {
            if (this.isAbleToClick && this.isPageEnter) this.toOpenModal(e);
          });
        });
    
        // 閉じるボタン
        this.closeList.forEach((ele) => {
          ele.addEventListener("click", (e) => {
            if (this.isAbleToClick && this.isPageEnter) this.toCloseModal();
          });
        });
      }
    }

    画面サイズをCSS変数として設定

    `resize` イベントで使用します。

    /**
     * @param {number} w // window.innerWidth
     * @param {number} h // window.innerHeight
     */
    export function SetPropertySize(w, h) {
      const vh = h * 0.01,
        longer = w > h ? w : h,
        shorter = w > h ? h : w;
    
      document.documentElement.style.setProperty("--vh", vh + "px"); // height: calc(var(--vh, 1vh) * 100);
      document.documentElement.style.setProperty("--longer", longer + "px");
      document.documentElement.style.setProperty("--shorter", shorter + "px");
    }
    

    Utility(lerp, delay, getParameter)

    export class Utility {
      /**
       * 線形補間
       * リファレンス:https://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E8%A3%9C%E9%96%93
       * @param {number} start
       * @param {number} end
       * @param {number} ease
       * @returns {number} startとendを補完した数値
       */
      lerp(start, end, ease) {
        return start * (1 - ease) + end * ease;
      }
    
      /**
       * 遅延 (await)
       * @param {number} time 遅延時間
       * @returns
       */
      delay(time) {
        return new Promise((resolve) => {
          setTimeout(() => {
            resolve();
          }, time);
        });
      }
    
      /**
       * GETパラメータのキーから値を取得し返す
       * 参考サイト:https://www-creators.com/archives/4463
       * @param {string} name // 取得したいGETパラメータのキー
       * @returns {string} // GETパラメータ値
       */
      getParameter(name) {
        name = name.replace(/[\[\]]/g, "\\$&");
        let regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
          results = regex.exec(window.location.href);
        if (!results) return null;
        if (!results[2]) return "";
        return decodeURIComponent(results[2].replace(/\+/g, " "));
      }
    }

    ©2025 SHOYA KAJITA.