この記事を作った動機

 JavaScript で何度か連発して起こった問題があったので、それを記録する。

問題点

 クラス内の関数を何かしらのコールバック関数として直接指定し、呼び出した場合に問題が起こる。その呼び出された関数内では、クラスに付属している変数を参照したり利用することができない。

import { tagListForIndexPage } from "./getTagInfo.js";

class sidebar{
    constructor(){
        new tagListForIndexPage()

        this.sidebarElem = document.querySelector(".sidebar")
        this.showAndHideButton = document.querySelector(".headMenu .headItemContainer .sidebarShowAndHide")

        this.currentState = "show"
        this.showAndHideButton.addEventListener("click",() => {
            if(this.currentState == "hide"){
                this.show()
            }else{
                this.hide()
            }
        })

        // 問題が起こらない部分
        // 問題が起こらない部分
        this.clacUI()
        // 問題が起こらない部分
        // 問題が起こらない部分

        // 問題の部分
        // 問題の部分
        addEventListener("resize",this.clacUI)
        // 問題の部分
        // 問題の部分
        
        this.hide()
    }

    clacUI(){
        // 直接クラス内から呼び出されたときは、this.sidebarElemは参照できるが、
        // クラス外のコールバックとして”直接”この関数を呼び出すと、this.sidebarElemが参照できずにundefinedでこける。
        this.sidebarElem.style.height = String(window.innerHeight - 60) + "px"
    }

    show(){
        this.currentState = "show"
        const replacement = this.sidebarElem.getAttribute("class").replaceAll(" hide","")
        this.sidebarElem.setAttribute("class",replacement)
    }
    hide(){
        this.currentState = "hide"
        let replacement = this.sidebarElem.getAttribute("class")
        if(!replacement.includes("hide")){
            replacement += " hide"
            this.sidebarElem.setAttribute("class",replacement)
        }
    }
}

new sidebar()

解決としたこと

 直接this.clacUIaddEventListenerなどのコールバックとして呼び出すのではなく、匿名関数を一つはさんでからその内部で関数を呼び出すようにした。

import { tagListForIndexPage } from "./getTagInfo.js";

class sidebar{
    constructor(){
        new tagListForIndexPage()

        this.sidebarElem = document.querySelector(".sidebar")
        this.showAndHideButton = document.querySelector(".headMenu .headItemContainer .sidebarShowAndHide")

        this.currentState = "show"
        this.showAndHideButton.addEventListener("click",() => {
            if(this.currentState == "hide"){
                this.show()
            }else{
                this.hide()
            }
        })

        // 問題が起こらない部分
        // 問題が起こらない部分
        this.clacUI()
        // 問題が起こらない部分
        // 問題が起こらない部分

        // 問題を修正した部分
        // 問題を修正した部分
        addEventListener("resize",() => {this.clacUI()})
        // 問題を修正した部分
        // 問題を修正した部分
        
        this.hide()
    }

    clacUI(){
        this.sidebarElem.style.height = String(window.innerHeight - 60) + "px"
    }

    show(){
        this.currentState = "show"
        const replacement = this.sidebarElem.getAttribute("class").replaceAll(" hide","")
        this.sidebarElem.setAttribute("class",replacement)
    }
    hide(){
        this.currentState = "hide"
        let replacement = this.sidebarElem.getAttribute("class")
        if(!replacement.includes("hide")){
            replacement += " hide"
            this.sidebarElem.setAttribute("class",replacement)
        }
    }
}

new sidebar()

わからないこと

 今回問題となった以下のコードの間における違いとしては、関数の呼び出し方によって見える変数や関数のスコープが異なるのではないかと考えられた。ただ具体的にじゃあ何のスコープに入っているのか?というのはすぐには見えてこなかった。

 うまく動かないコードでは、グローバルスコープを呼び出されたときに見ていてクラス内のスコープがうまく見えず、うまく動くコードでは、クラス内のスコープを見ているという違いはあるかもしれないとは考えられた。

class sidebar{
    constructor(){
    ...
        // うまく動かないコード
        addEventListener("resize",this.clacUI)
    ...
    }
}
class sidebar{
    constructor(){
    ...
        // うまく動くコード
        addEventListener("resize",() => {this.clacUI()})
    ...
    }
}

参考にしたサイトとか