如何让 IntersectionObserver 使用 div 个 ID 的节点列表或 div 个 ID 的数组?

How to get IntersectionObserver to work with a nodeList of div IDs or array of div IDs?

我正在创建一个网站,该网站在同一个 HTML 页面上有多个“章节”。

每一章的顶部都有两个跳过按钮。左跳过按钮跳转到上一章。右跳键跳转到下一章

我希望 SkipButton 仅在靠近视口顶部时获得 0.5 或 1.0 的不透明度。 (否则它们的不透明度为 0.0。)

我想出了如何为每个 skipButtonId 创建一个 IntersectionObserver;但是代码超级丑陋和冗余。

观察者观察到的每个元素都是我要为其更改类名的相同元素。

如何重写代码使只有一个观察者?

理想情况下,我想从具有 className trailingArrowsClass 的所有 skipButtonId 的 NodeList 开始工作。

或者,如果有理由这样做,从转换为数组的 NodeList 开始工作。

或者,如果这些选择都不起作用,则从手动创建的 div ID 数组开始工作。

我试过的都没有用。请提供专业知识!

下面粘贴了整个 HTML 页面的代码。

工作演示页面位于 https://jerrymarlow.com/intersectionobserver

如果您在浏览器中查看代码或演示页面,控制台将记录: o 节点列表 o NodeList 转换为数组 o 手动创建的 skipButtonIds

数组

谢谢! 杰瑞

<!doctype html>
<html>

<head>
    <meta charset="UTF-8">
    <title>How to get IntersectionObserver to work with a nodeList of div IDs or array of div IDs?</title>

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

    <style>
        /* This section of CSS is for styling only. It does not affect IntersectionObserver. */
        .containerDivClass {
            width: auto;
            margin: auto;
            background-color: lightgray;
        }

        .chapterContainerClass {
            background-color: white;
            margin: auto;
            max-width: 400px;
            padding: 0px;
            width: 100%;
            max-width: 400px;
            border-bottom: solid 3px rgba(230, 230, 230, 1.00);
            margin-bottom: 20px;
        }

        .chapterHeaderClass {
            box-sizing: border-box;
            background-color: #F8F8F8;
            background-color: white;
            margin: 0px;
            padding: 14px;
            border-top: thick;
            border-top-color: currentcolor;
            border-bottom: 5px #F0E008;
            border-bottom-color: rgb(240, 224, 8);
            border-bottom-style: none;
            border-bottom-style: solid;
            border-color: #990101;
        }

        h2 {
            color: #990101;
        }

        .storyTellerClass {
            padding: 14px;
        }

        .skipButtonsContainerClass {
            box-sizing: border-box;
            width: 100%;
            height: 35px;
            background-color: #990101;
        }


        .skipLeftButtonClass,
        .skipRightButtonClass {
            width: 105px;
            height: 35px;
            margin-top: 0px;
            margin-bottom: 0px;
            background-size: 105px 35px;
        }

        .skipLeftButtonClass,
        .skipLeftButtonClass:hover {
            float: left;
            margin-left: 0px;
        }

        .skipLeftButtonClass {
            background-image: url(https://jerrymarlow.com/images/skip_left.jpg)
        }

        .skipLeftButtonClass:hover {
            background-image: url(https://jerrymarlow.com/images/skip_left_hover.jpg)
        }

        .skipRightButtonClass,
        .skipRightButtonClass:hover {
            float: right;
            margin-right: 0px;
        }

        .skipRightButtonClass {
            background-image: url(https://jerrymarlow.com/images/skip_right.jpg)
        }

        .skipRightButtonClass:hover {
            background-image: url(https://jerrymarlow.com/images/skip_right_hover.jpg)
        }

        .spacerClass {
            height: 70px;
        }
    </style>
    <style>
        /* IntersectionObserver uses this section of CSS to change the opacity of the skip buttons.  */

        .skipButtonOpacity0 {
            opacity: 0;
            pointer-events: none;
        }

        .skipButtonOpacity50 {
            opacity: 0.5;
            pointer-events: auto;
        }

        .skipButtonOpacity100 {
            opacity: 1;
            pointer-events: auto;
        }
    </style>
</head>

<body>
    <main>
        <div id="containerDivID" class="containerDivClass">
            <div class="spacerClass"> </div>

            <div class="chapterContainerClass" id="AlohaChapterID">
                <div class="singleColumnContainerClass">

                    <div class="skipButtonsContainerClass">
                        <a href="#DingoChapterID">
                            <div id="AlohaLeftskipButtonId" class="skipLeftButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                        <a href="#BravoChapterID">
                            <div id="AlohaRightskipButtonId" class="skipRightButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                    </div>

                    <div class="chapterHeaderClass">
                        <h2>
                            Aloha Chapter
                        </h2>
                    </div>
                    <div class="storyTellerClass">
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                        <p> Aloha, aloha, aloha. </p>
                    </div>
                </div>
            </div>

            <div class="chapterContainerClass" id="Bravo_left_you_upset_rattled_or_traumatized_think">
                <div class="singleColumnContainerClass">

                    <div class="skipButtonsContainerClass">
                        <a href="#AlohaChapterID">
                            <div id="BravoLeftskipButtonId" class="skipLeftButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                        <a href="#CandyChapterID">
                            <div id="BravoRightskipButtonId" class="skipRightButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                    </div>

                    <div class="chapterHeaderClass">
                        <h2>Bravo Chapter
                        </h2>
                    </div>
                    <div class="storyTellerClass">
                        <p> Bravo, bravo, bravo. </p>
                        <p> Bravo, bravo, bravo. </p>
                        <p> Bravo, bravo, bravo. </p>
                        <p> Bravo, bravo, bravo. </p>
                        <p> Bravo, bravo, bravo. </p>
                    </div>
                </div>
            </div>


            <div class="chapterContainerClass" id="CandyChapterID">
                <div class="singleColumnContainerClass">
                    <div class="skipButtonsContainerClass">
                        <a href="#Bravo_left_you_upset_rattled_or_traumatized_think">
                            <div id="CandyLeftskipButtonId" class="skipLeftButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                        <a href="#DingoChapterID">
                            <div id="CandyRightskipButtonId" class="skipRightButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                    </div>
                    <div class="chapterHeaderClass">
                        <h2> Candy Chapter </h2>
                    </div>
                    <div class="storyTellerClass">
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                        <p> Candy, candy, candy. </p>
                    </div>
                </div>
            </div>

            <div class="chapterContainerClass" id="DingoChapterID">
                <div class="singleColumnContainerClass">
                    <div class="skipButtonsContainerClass">
                        <a href="#CandyChapterID">
                            <div id="DingoLeftskipButtonId" class="skipLeftButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                        <a href="#AlohaChapterID">
                            <div id="DingoRightskipButtonId" class="skipRightButtonClass trailingArrowClass skipButtonOpacity0">
                            </div>
                        </a>
                    </div>
                    <div class="chapterHeaderClass">
                        <h2> Dingo Chapter</h2>
                    </div>
                    <div class="storyTellerClass">
                        <p> Dingo, dingo, dingo. </p>
                        <p> Dingo, dingo, dingo. </p>
                        <p> Dingo, dingo, dingo. </p>
                        <p> Dingo, dingo, dingo. </p>
                        <p> Dingo, dingo, dingo. </p>
                        <p> Dingo, dingo, dingo. </p>
                    </div>
                </div>
            </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
            <div class="spacerClass"> </div>
        </div>
    </main>

    <script>
        var AlohaLeftskipButtonId;
        var AlohaRightskipButtonId;
        var BravoLeftskipButtonId;
        var BravoRightskipButtonId;
        var CandyLeftskipButtonId;
        var CandyRightskipButtonId;
        var DingoLeftskipButtonId;
        var DingoRightskipButtonId;


        var trailingArrowsNodeList;
        var trailingIDsArray;
        var skipButtonsIDsArray;

        window.addEventListener("load", function() {

            // This demo web page has four "chapters."
            // At the top of each chapter is a LeftSkipButton that jumps the reader back one chapter
            // and a RightSkipButton that jumps the reader ahead one chapter. 

            AlohaLeftskipButtonId = document.getElementById("AlohaLeftskipButtonId");
            AlohaRightskipButtonId = document.getElementById("AlohaRightskipButtonId");
            BravoLeftskipButtonId = document.getElementById("BravoLeftskipButtonId");
            BravoRightskipButtonId = document.getElementById("BravoRightskipButtonId");
            CandyLeftskipButtonId = document.getElementById("CandyLeftskipButtonId");
            CandyRightskipButtonId = document.getElementById("CandyRightskipButtonId");
            DingoLeftskipButtonId = document.getElementById("DingoLeftskipButtonId");
            DingoRightskipButtonId = document.getElementById("DingoRightskipButtonId");

            // I want the SkipButtons to get 0.5 or 1.0 opacity only when they are near the top of the viewport. 
            // (Otherwise they have opacities of 0.0.)


            // I figured out how to create an IntersectionObserver for each skipButtonId. 
            // But the code is super ugly and redundant. 
            // Each element that an observer observes is the same element for which I want to change the className.
            // How can I rewrite the code so that there is only one observer?

            const AlohaLeftskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    AlohaLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    AlohaLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    AlohaLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            AlohaLeftskipButtonObserver.observe(AlohaLeftskipButtonId);


            const AlohaRightskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    AlohaRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    AlohaRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    AlohaRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            AlohaRightskipButtonObserver.observe(AlohaRightskipButtonId);



            const BravoLeftskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    BravoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    BravoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    BravoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            BravoLeftskipButtonObserver.observe(BravoLeftskipButtonId);


            const BravoRightskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    BravoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    BravoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    BravoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            BravoRightskipButtonObserver.observe(BravoRightskipButtonId);



            const CandyLeftskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    CandyLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    CandyLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    CandyLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            CandyLeftskipButtonObserver.observe(CandyLeftskipButtonId);


            const CandyRightskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    CandyRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    CandyRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    CandyRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            CandyRightskipButtonObserver.observe(CandyRightskipButtonId);


            const DingoLeftskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    DingoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    DingoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    DingoLeftskipButtonId.className = "skipLeftButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            DingoLeftskipButtonObserver.observe(DingoLeftskipButtonId);


            const DingoRightskipButtonObserver = new IntersectionObserver(function(entries) {
                if (entries[0].intersectionRatio < 0.5) {
                    DingoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity0";
                }
                if (entries[0].intersectionRatio >= 0.5 &&
                    entries[0].intersectionRatio < 1.0) {
                    DingoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity50";
                }
                if (entries[0].intersectionRatio >= 1.0) {
                    DingoRightskipButtonId.className = "skipRightButtonClass skipButtonOpacity100";
                }
            }, {
                "threshold": [0, 0.5, 1.0],
                "rootMargin": "-50px 0px -67% 0px"
            });
            DingoRightskipButtonObserver.observe(DingoRightskipButtonId);


            
            // Ideally, I would like to work from a trailingArrowsNodeList:
            trailingArrowsNodeList = document.querySelectorAll(".trailingArrowClass");
            console.log("trailingArrowsNodeList " + trailingArrowsNodeList);

            for (var entry of trailingArrowsNodeList.entries()) {
                console.log(entry);
            }

            // Or, if there is a reason to do so, work from that NodeList converted to an array: 
            var trailingArrowsArray = Array.from(trailingArrowsNodeList);
            console.log("trailingArrowsArray " + trailingArrowsArray);

            for (var entry of trailingArrowsArray.entries()) {
                console.log(entry);
            }


            // Or, if neither of those choices work, work from a manually created array of div IDs: 
            const skipButtonsIDsArray = [AlohaLeftskipButtonId, AlohaRightskipButtonId, BravoLeftskipButtonId, BravoRightskipButtonId, CandyLeftskipButtonId, CandyRightskipButtonId, DingoLeftskipButtonId, DingoRightskipButtonId];
            console.log("skipButtonsIDsArray " + skipButtonsIDsArray);

            for (var entry of skipButtonsIDsArray.entries()) {
                console.log(entry);
            }
            
            // Nothing I've tried worked. Your expertise please!
            // Thank you. 
            // Jerry 
        });
    </script>
</body>
</html>
//E[foo$="bar"]     an E element whose "foo" attribute value ends exactly with the string "bar"     Attribute selectors     3
//div[id$="skipButtonId"]

mybuttons = document.querySelectorAll("div[id$='skipButtonId']");

const mybuttonObserver = new IntersectionObserver(function(entries){

console.log("Num entries= " + entries.length);


entries.forEach((n) => {
// I want the SkipButtons to get 0.5 or 1.0 opacity only when they are near the top of the viewport. 
// (Otherwise they have opacities of 0.0.)


if (n.intersectionRatio < 0.5) {
    n.target.classList.remove("skipButtonOpacity50", "skipButtonOpacity100");
    n.target.classList.add("skipButtonOpacity0");
    console.log("io = <0.5 - ",n.target.classList);
}

if(n.intersectionRatio>=0.5 && n.intersectionRatio < 1.0){
    n.target.classList.remove("skipButtonOpacity0","skipButtonOpacity100");
    n.target.classList.add("skipButtonOpacity50");
    console.log("io = >=0.5  and <1.0" , n.target.classList);
}   

if (n.intersectionRatio >= 1.0) {
    n.target.classList.remove("skipButtonOpacity0","skipButtonOpacity50");
    n.target.classList.add("skipButtonOpacity100");
    console.log("io = >=1 " , n.target.classList);
}



});


},{ 
    threshold  : [0,0.5,1],
    rootMargin : "-50px 0px -67% 0px"
});




for (var e of mybuttons){
    mybuttonObserver.observe(e);
}