【備忘録】ビデオ要素の動作を制御し、クリック時にレイアウトやスタイルを変更する

# やりたい事

ビデオをクリック時にレイアウトやスタイルを変更する


# 書いたJavaScript

```ruby:JavaScript
document.addEventListener('DOMContentLoaded', function () {
    const videosPC = document.querySelectorAll('.topbrandforPC video');
    const containers = document.querySelectorAll('.topbrand_contents_inner');
    const nonWorkingContainer = document.querySelector('.topbrand_not_working');
    const nonWorkingWidth = '10%';
    const activeWidth = '60%';
    const inactiveWidth = '20%';
    const defaultNonWorkingWidth = ${100 - (2 * 40)}%; // 20%
    const videosSP = document.querySelectorAll('.topbrandforSP video');

    // 初期状態を設定
    containers.forEach(container => {
        if (container.classList.contains('topbrand_not_working')) {
            container.style.width = defaultNonWorkingWidth;
        } else {
            container.style.width = '40%';
        }
    });

    videosPC.forEach((video, index) => {
        if (video.closest('.topbrand_not_working')) {
            // topbrand_not_working内の動画コントロールを常に非表示に設定
            video.controls = false;
            video.addEventListener('mouseenter', () => video.controls = false);
            video.addEventListener('mousemove', () => video.controls = false);
            video.addEventListener('mouseleave', () => video.controls = false);
            return; // topbrand_not_workingクラスを持つコンテナはスキップ
        }

        const container = containers[index];
        const article = container.querySelector('.topbrand_article');

        video.addEventListener('click', () => {
            videosPC.forEach((v, i) => {
                const otherContainer = containers[i];
                const otherArticle = otherContainer.querySelector('.topbrand_article');

                if (v === video) {
                    otherContainer.style.width = activeWidth;
                    otherContainer.style.filter = 'none';
                    otherArticle.style.display = 'none'; // topbrand_articleを非表示
                    otherContainer.classList.add('video_start');
                    v.play();
                } else if (!otherContainer.classList.contains('topbrand_not_working')) {
                    otherContainer.style.width = inactiveWidth;
                    otherContainer.style.filter = 'brightness(0.5)';
                    otherArticle.style.display = 'block'; // topbrand_articleを表示
                    otherContainer.classList.remove('video_start');
                    v.pause();
                }
            });
            nonWorkingContainer.style.width = nonWorkingWidth;
            nonWorkingContainer.style.filter = 'brightness(0.5)';
        });

        // 再生停止時の処理
        video.addEventListener('pause', function () {
            if (!this.seeking) {
                containers.forEach(container => {
                    const article = container.querySelector('.topbrand_article');
                    if (container.classList.contains('topbrand_not_working')) {
                        container.style.width = defaultNonWorkingWidth;
                        container.style.filter = 'none';
                    } else {
                        container.style.width = '40%';
                        container.style.filter = 'none';
                        article.style.display = 'block'; // topbrand_articleを表示
                        container.classList.remove('video_start');
                    }
                });
            }
        });

        // 再生終了時の処理
        video.addEventListener('ended', () => {
            containers.forEach(container => {
                const article = container.querySelector('.topbrand_article');
                if (container.classList.contains('topbrand_not_working')) {
                    container.style.width = defaultNonWorkingWidth;
                    container.style.filter = 'none';
                } else {
                    container.style.width = '40%';
                    container.style.filter = 'none';
                    article.style.display = 'block'; // topbrand_articleを表示
                    container.classList.remove('video_start');
                }
            });
        });
    });

});
```
:::note info
# 全体の流れ

* 1.ページの読み込み完了時に初期設定を行う
* 2.初期状態で各コンテナの幅を設定する
* 3.ビデオ要素のクリック時に、他のビデオやコンテナの状態を変更する
* 4.ビデオの再生停止時と終了時にコンテナの状態を元に戻す
:::


## 1.ページの読み込み完了時に初期設定を行う。
```ruby:JavaScript
document.addEventListener('DOMContentLoaded', function () {
    const videosPC = document.querySelectorAll('.topbrandforPC video');
    const containers = document.querySelectorAll('.topbrand_contents_inner');
    const nonWorkingContainer = document.querySelector('.topbrand_not_working');
    const nonWorkingWidth = '10%';
    const activeWidth = '60%';
    const inactiveWidth = '20%';
    const defaultNonWorkingWidth = `${100 - (2 * 40)}%`; // 20%
    const videosSP = document.querySelectorAll('.topbrandforSP video');

```
・document.addEventListener('DOMContentLoaded', function () {...}): ページの読み込みが完了したときに実行される関数を設定。
・const videosPC, const containers, const nonWorkingContainerなど: 各要素を取得し変数に格納。


## 2. 初期状態で各コンテナの幅を設定する。
```ruby:JavaScript
      containers.forEach(container => {
        if (container.classList.contains('topbrand_not_working')) {
            container.style.width = defaultNonWorkingWidth;
        } else {
            container.style.width = '40%';
        }
    });

```


## 3. ビデオ要素のクリック時に、他のビデオやコンテナの状態を変更する。
```ruby:JavaScript
      videosPC.forEach((video, index) => {
        if (video.closest('.topbrand_not_working')) {
            video.controls = false;
            video.addEventListener('mouseenter', () => video.controls = false);
            video.addEventListener('mousemove', () => video.controls = false);
            video.addEventListener('mouseleave', () => video.controls = false);
            return;
        }

        const container = containers[index];
        const article = container.querySelector('.topbrand_article');

        video.addEventListener('click', () => {
            videosPC.forEach((v, i) => {
                const otherContainer = containers[i];
                const otherArticle = otherContainer.querySelector('.topbrand_article');

                if (v === video) {
                    otherContainer.style.width = activeWidth;
                    otherContainer.style.filter = 'none';
                    otherArticle.style.display = 'none';
                    otherContainer.classList.add('video_start');
                    v.play();
                } else if (!otherContainer.classList.contains('topbrand_not_working')) {
                    otherContainer.style.width = inactiveWidth;
                    otherContainer.style.filter = 'brightness(0.5)';
                    otherArticle.style.display = 'block';
                    otherContainer.classList.remove('video_start');
                    v.pause();
                }
            });
            nonWorkingContainer.style.width = nonWorkingWidth;
            nonWorkingContainer.style.filter = 'brightness(0.5)';
        });

```
・video.addEventListener('click', () => {...}): ビデオがクリックされたときの動作を設定。
・クリックされたビデオは再生し、他のビデオは一時停止し、各コンテナの幅やスタイルを変更。


## 4.ビデオの再生停止時と終了時にコンテナの状態を元に戻す。
```ruby:JavaScript
          video.addEventListener('pause', function () {
            if (!this.seeking) {
                containers.forEach(container => {
                    const article = container.querySelector('.topbrand_article');
                    if (container.classList.contains('topbrand_not_working')) {
                        container.style.width = defaultNonWorkingWidth;
                        container.style.filter = 'none';
                    } else {
                        container.style.width = '40%';
                        container.style.filter = 'none';
                        article.style.display = 'block';
                        container.classList.remove('video_start');
                    }
                });
            }
        });

        video.addEventListener('ended', () => {
            containers.forEach(container => {
                const article = container.querySelector('.topbrand_article');
                if (container.classList.contains('topbrand_not_working')) {
                    container.style.width = defaultNonWorkingWidth;
                    container.style.filter = 'none';
                } else {
                    container.style.width = '40%';
                    container.style.filter = 'none';
                    article.style.display = 'block';
                    container.classList.remove('video_start');
                }
            });
        });
    });

});

```
新しいスクリプト要素を作成し、そのsrc属性に指定されたURLを設定し、IDをlpcats_cv_scriptに設定します。

:::note info
# メソッドと関数の意味
:::


## document.addEventListener('DOMContentLoaded', function () {...})
```ruby:JavaScript
document.addEventListener('DOMContentLoaded', function () {
    // ページが完全に読み込まれた後に実行されるコードをここに書く
});
```
意味: ページの全てのHTMLが読み込まれた後に、指定された関数を実行します。これにより、DOM要素に安全にアクセスし、操作することができます。

## document.querySelectorAll(selector)
```ruby:JavaScript
const videosPC = document.querySelectorAll('.topbrandforPC video');
```
意味: 指定されたCSSセレクターに一致する全ての要素をNodeListとして返します。ここでは、クラス名がtopbrandforPCのビデオ要素を全て取得します。



## document.querySelector(selector)
```ruby:JavaScript
const nonWorkingContainer = document.querySelector('.topbrand_not_working');
```
意味: 指定されたCSSセレクターに一致する最初の要素を返します。ここでは、クラス名がtopbrand_not_workingの要素を取得します。



## element.querySelector(selector)
```ruby:JavaScript
const article = container.querySelector('.topbrand_article');
```
意味: 指定されたCSSセレクターに一致する最初の子要素を返します。ここでは、各コンテナ内のクラス名がtopbrand_articleの要素を取得します。


## element.addEventListener(event, function)
```ruby:JavaScript
video.addEventListener('click', () => {
    // ビデオがクリックされたときに実行するコード
});

```
意味: 指定されたイベントが発生したときに、指定された関数を実行します。ここでは、ビデオがクリックされたときの処理を設定しています。

## element.classList.contains(className)
```ruby:JavaScript
if (container.classList.contains('topbrand_not_working')) {
    // 要素がクラス`topbrand_not_working`を持っているかをチェック
}

```
意味: 要素が指定されたクラスを持っているかを確認し、真偽値を返します。ここでは、コンテナがtopbrand_not_workingクラスを持っているかをチェックしています。


## element.style.property
```ruby:JavaScript
container.style.width = '40%';

```
意味: 要素のスタイルプロパティを設定します。ここでは、コンテナの幅を40%に設定しています。

## video.play()
```ruby:JavaScript
video.play();
```
意味: ビデオの再生を開始します。


## video.pause()
```ruby:JavaScript
video.pause();
```
意味: ビデオの再生を一時停止します。


## コンテナの初期幅を設定
```ruby:JavaScript
containers.forEach(container => {
    if (container.classList.contains('topbrand_not_working')) {
        container.style.width = defaultNonWorkingWidth;
    } else {
        container.style.width = '40%';
    }
});

```
意味: 各コンテナの初期幅を設定します。topbrand_not_workingクラスを持つコンテナはdefaultNonWorkingWidth、それ以外は40%に設定します。


## ビデオがtopbrand_not_workingクラスを持つコンテナ内にある場合の処理
```ruby:JavaScript
if (video.closest('.topbrand_not_working')) {
    video.controls = false;
    video.addEventListener('mouseenter', () => video.controls = false);
    video.addEventListener('mousemove', () => video.controls = false);
    video.addEventListener('mouseleave', () => video.controls = false);
    return;
}

```
意味: ビデオがtopbrand_not_workingクラスを持つコンテナ内にある場合、ビデオのコントロールを常に非表示に設定し、以降の処理をスキップします。


## ビデオのクリック時の動作
```ruby:JavaScript
video.addEventListener('click', () => {
    videosPC.forEach((v, i) => {
        const otherContainer = containers[i];
        const otherArticle = otherContainer.querySelector('.topbrand_article');

        if (v === video) {
            otherContainer.style.width = activeWidth;
            otherContainer.style.filter = 'none';
            otherArticle.style.display = 'none';
            otherContainer.classList.add('video_start');
            v.play();
        } else if (!otherContainer.classList.contains('topbrand_not_working')) {
            otherContainer.style.width = inactiveWidth;
            otherContainer.style.filter = 'brightness(0.5)';
            otherArticle.style.display = 'block';
            otherContainer.classList.remove('video_start');
            v.pause();
        }
    });
    nonWorkingContainer.style.width = nonWorkingWidth;
    nonWorkingContainer.style.filter = 'brightness(0.5)';
});
```
意味: ビデオがクリックされたときに、クリックされたビデオは再生し、それ以外のビデオは一時停止します。また、各コンテナの幅やフィルターを変更し、対応する記事要素の表示/非表示を切り替えます。


## ビデオの再生停止時と終了時の動作
```ruby:JavaScript
video.addEventListener('pause', function () {
    if (!this.seeking) {
        containers.forEach(container => {
            const article = container.querySelector('.topbrand_article');
            if (container.classList.contains('topbrand_not_working')) {
                container.style.width = defaultNonWorkingWidth;
                container.style.filter = 'none';
            } else {
                container.style.width = '40%';
                container.style.filter = 'none';
                article.style.display = 'block';
                container.classList.remove('video_start');
            }
        });
    }
});

video.addEventListener('ended', () => {
    containers.forEach(container => {
        const article = container.querySelector('.topbrand_article');
        if (container.classList.contains('topbrand_not_working')) {
            container.style.width = defaultNonWorkingWidth;
            container.style.filter = 'none';
        } else {
            container.style.width = '40%';
            container.style.filter = 'none';
            article.style.display = 'block';
            container.classList.remove('video_start');
        }
    });
});

```
意味: ビデオの再生が停止または終了したときに、全てのコンテナの幅やフィルターを元に戻し、記事要素を表示します。