Fixing jslint errors

How are these fixed?

How many times are you allowed to use iife?

I would need to give those a different name.

I don’t understand 3.

  const playButtons = document.querySelectorAll(".button");
  playButtons.forEach(function addHandler(el) {
    el.addEventListener("click", playButtonClickHandler);
  });
})();

Also 1.

  1. Don’t wrap function literals in parens.

I changed it from iife to:

(function manageButtons() {
    "use strict";

and I still get: “Don’t wrap function literals in parens.”

https://jsfiddle.net/cr5xzjv2/4/

(function manageCovera() {
    "use strict";

    function hide(el) {
        el.classList.add("hide");
    }

    function coverClickHandler(evt) {
        const cover = evt.currentTarget;
        hide(cover);
    }

    const cover = document.querySelector(".playa");
    cover.addEventListener("click", coverClickHandler);
}());

const videoPlayer = (function makeVideoPlayer() {
    "use strict";

    let player = null;

    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);

    function shufflePlaylist(player) {
        player.setShuffle(true);
        player.playVideoAt(0);
        player.pauseVideo();
    }

    function onPlayerReady(event) {
        player = event.target;
        player.setVolume(100); // percent
        shufflePlaylist(player);
    }

    function addPlayer(video) {

        const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";
        const config = {
            height: 360,
            host: "https://www.youtube-nocookie.com",
            width: 640
        };
        config.playerVars = {
            autoplay: 0,
            cc_load_policy: 0,
            controls: 1,
            disablekb: 1,
            fs: 0,
            iv_load_policy: 3,
            loop: 1,
            playlist,
            rel: 0
        };
        config.events = {
            "onReady": onPlayerReady
        };
        player = new YT.Player(video, config);

    }

    function play() {
        player.playVideo();
    }
    return {
        addPlayer,
        play
    };
}());

function onYouTubeIframeAPIReady() {
    const cover = document.querySelector(".playa");
    const wrapper = cover.parentElement;
    const frameContainer = wrapper.querySelector(".video");
    videoPlayer.addPlayer(frameContainer);
}

(function iife() {
    "use strict";

    function show(el) {
        el.classList.remove("hide");
    }

    function hide(el) {
        el.classList.add("hide");
    }

    function coverClickHandler(evt) {
        const cover = evt.currentTarget;
        const theplay = cover.parentElement.querySelector(".playb");
        hide(theplay);
        const thewrap = cover.parentElement.querySelector(".containera");
        show(thewrap);
        videoPlayer.play();
    }

    const cover = document.querySelector(".playa");
    cover.addEventListener("click", coverClickHandler);
}());

(function manageCoverb() {
    "use strict";

    function hide(el) {
        el.classList.add("hide");
    }

    function coverClickHandler(evt) {
        const cover = evt.currentTarget;
        hide(cover);
    }

    const cover = document.querySelector(".playb");
    cover.addEventListener("click", coverClickHandler);
}());

(function iife() {
    "use strict";

    function getButtonContainer(el) {
        while (el.classList.contains("playButton") === false) {
            el = el.parentNode;
        }
        return el;
    }

    function getPlay(button) {
        return button;
    }

    function showPlayButton(button) {
        button.classList.remove("active");
    }

    function isPlaying(button) {
        const play = getPlay(button);
        return play.classList.contains("active");
    }

    function pauseAllButtons() {
        let buttons = document.querySelectorAll(".playButton");
        buttons.forEach(function hidePause(buttons) {
            if (isPlaying(buttons)) {
                showPlayButton(buttons);
            }
        });
    }

    function showPauseButton(button) {
        pauseAllButtons();
        button.classList.add("active");
    }

    function getAudio() {
        return document.querySelector("audio");
    }

    function playAudio(player, src) {
        player.volume = 1.0;
        if (player.getAttribute("src") !== src) {
            player.setAttribute("src", src);
        }
        player.play();
    }

    function showButton(button, opts) {
        if (opts.playing) {
            showPlayButton(button);
        } else {
            showPauseButton(button);
        }
    }

    function pauseAudio(player) {
        player.pause();
    }

    function manageAudio(player, opts) {
        if (opts.playing) {
            pauseAudio(player);
        } else {
            playAudio(player, opts.src);
        }
    }

    function playButton(button) {
        const player = getAudio();
        const playing = isPlaying(button);
        showButton(button, {
            playing
        });
        manageAudio(player, {
            playing,
            src: button.getAttribute("data-audio")
        });
    }

    function playButtonClickHandler(evt) {
        const button = getButtonContainer(evt.target);
        playButton(button);
    }

    const playButtons = document.querySelectorAll(".button");
    playButtons.forEach(function addHandler(el) {
        el.addEventListener("click", playButtonClickHandler);
    });
})();

(function iife() {
    "use strict";

    function show(el) {
        el.classList.remove("hide");
    }

    function hide(el) {
        el.classList.add("hide");
    }

    function coverClickHandler(evt) {
        const cover = evt.currentTarget;
        const theplay = cover.parentElement.querySelector(".playa");
        hide(theplay);
        const thewrap = cover.parentElement.querySelector(".containerb");
        show(thewrap);
    }

    const cover = document.querySelector(".playb");
    cover.addEventListener("click", coverClickHandler);
}());

Function literals

Here’s the top and bottom of the iife in question:

(function iife() {
    ...
})();

At the bottom, after the closing brace and the closing parenthesis, are the invoking parenthesis that look like (). Those are disliked after the parenthesis because it’s not supposed to be the parenthesis that is being invoked, it is the function that is being invoked. The invoking () are to be moved inside of the closing parenthesis so that the invoking () come after the closing brace instead.

(function iife() {
    ...
}());

Duplicate iife

You have two iife sections. We need to ask why do we have two there instead of one? It’s because one of them groups some things together separately from the other set of things. Using the name iife is just a generic term that’s used on example code. We should rename them so that they more clearly inform us about what is inside.

The first one initializes the play buttons, and the second one initializes the cover, so until better names come to mind, we can name them as follows:

(function initPlayButtons() {
    ...
}());

(function initCover() {
    ...
}());
1 Like
  1. Don’t wrap function literals in parens.
    (function initPlayButtons() {

I just received that error.

https://jsfiddle.net/r8qwhncp/

There seems to be some things that you don’t understand. I’ll try to spell them out for you.

  • Function literal: Is a function that starts with an opening brace, and ends with a closing brace.
    Example: function initPlayButtons() { … }
  • Parens: Are the opening and closing parenthesis that look like this: ( ... )

When it says: “Don’t wrap function literals in parens” that is exactly what it means.

Here is a function literal wrapped in parens:

(function initPlayButtons() {
    ...
}); // function literal wrapped in parens = bad

The following is also a function literal wrapped in parens:

(function initPlayButtons() {
    ...
})(); // function literal wrapped in parens and then invoked = bad

However, the following is not a function literal wrapped in parens, because the invoking parenthesis after the closing brace invoke the function. That causes it to no longer be a function literal, and is replaced with whatever is returned from the function.

(function initPlayButtons() {
    ...
}()); // function literal invoked and wrapped in parens = good

As you might have noticed, that error doesn’t really have anything to do with the start of the function, but is all about how it ends.

1 Like

Something has to be changed in how this ends?

    const playButtons = document.querySelectorAll(".button");
    playButtons.forEach(function addHandler(el) {
        el.addEventListener("click", playButtonClickHandler);
    });
})();
  1. Wrap an immediate function invocation in parentheses to assist the reader in understanding that the expression is the result of a function, and not the function itself.
    })();
1 Like

The guy that created JSLint said that a good way to remember which way is good and which way is bad is that the bad way has the invoking () looking like dog balls hanging off the end.

})(); = bad

Whereas the good way has the invoking () nicely contained in the parenthesis.

}()); = good

2 Likes

Should strict be placed under it?

(function initPlayButtons() {
   "use strict";

If no, how do you know when it is needed, and when it is not?

There is conflict on which way is to be used. JSLint has gone with one way and not the other.
The way that JSLint has gone is also consistent with how functions are used in general.

Here is a function declaration:

function add() {
    ...
}

To invoke that add function, you place the invoking parenthesis directly after the function name.

add();

When it’s a function literal exactly the same thing occurs. First there’s the function, and we invoke it.

function add() {
    ...
}();

The problem with the above though is that JavaScript does not allow a function literal to start a line, because JS can confuse that for a function declaration instead.

To alleviate that problem, we place the whole thing in parenthesis, so that the add function becomes a function literal instead. Literal here just means that the function is inline with the code, instead of being declared separately.

(function add() {
    ...
}());

Even though this next variation is syntactically correct, it breaks the pattern of the declaration happening directly after the function.

(function add() {
    ...
})();

That’s why JSLint requires you to place the invoking parenthesis directly after the function instead.

(function add() {
    ...
}());
1 Like

“use strict” is used on all top-level functions.

A good coding practice is to place all of your functions inside of one outer function, so that “use strict” is only needed on that outer function.

You have several of those outer functions in your code, so each one has “use strict” on them.

1 Like

I should only need 2 then.
https://jsfiddle.net/7aL5put8/

(function manageCovera() {
    "use strict";

const videoPlayer = (function makeVideoPlayer() {

(function initCovera(){

(function manageCoverb() {
    "use strict";

(function initPlayButtons() {

(function initCoverb() {

It looks like you’ve got six there.

stricts, I only have 2.

However, about “use strict” - JSLint doesn’t require that anymore when using a browser, because there are virtually no old browsers being used for that to provide much benefit anymore.

oh, I didn’t know that, thank you for telling me.

You mean like this.
https://jsfiddle.net/7ztg9koh/3/


(function manageCovera() {

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    hide(cover);
    const theplay = cover.parentElement.querySelector(".playb");
    hide(theplay);
    const thewrap = cover.parentElement.querySelector(".containera");
    show(thewrap);
  }

  const cover = document.querySelector(".playa");
  cover.addEventListener("click", coverClickHandler);
}());

(function initCover() {

  function coverClickHandler() {
    videoPlayer.play();
  }

  const cover = document.querySelector(".playa");
  cover.addEventListener("click", coverClickHandler);
}());

(function manageCoverb() {

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    hide(cover);
    const theplay = cover.parentElement.querySelector(".playa");
    hide(theplay);
    const thewrap = cover.parentElement.querySelector(".containerb");
    show(thewrap);
  }

  const cover = document.querySelector(".playb");
  cover.addEventListener("click", coverClickHandler);
}());
1 Like

How can I make this line shorter than 80 characters?

    const covers = document.querySelectorAll(".jacket-left, .jacket-middle, .jacket-right");
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });
}());

The function parameters can be put on a separate line.

    const covers = document.querySelectorAll(
        ".jacket-left, .jacket-middle, .jacket-right"
    );
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });

However, if that string of jackets get larger, then other techniques can be used.

One is to use an array for each jacket selector, and another is to search the DOM for the elements instead.

1 Like

This is another way I figured out:

    const coversSelector = ".jacket-left, .jacket-middle, .jacket-right";
    const covers = document.querySelectorAll(coversSelector);
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });
}());

Yes, that is another approach that I have done in other work on the code.

Once again, it is a temporary solution that needs to change when there are more covers to select.
Currently it works and makes it pretty clear what’s going on, but that’s conditional on only having a few elements to select.

When the list gets larger you can put them into an array.

    const coverSelectors = [
        ".jacket-topleft",
        ".jacket-topcenter",
        ".jacket-topright",
        ".jacket-middleleft",
        ".jacket-middlecenter",
        ".jacket-middleright",
        ".jacket-bottomleft",
        ".jacket-bottomcenter",
        ".jacket-bottomright"
    ];
    const covers = coverSelectors.map(function (coverSelector) {
        return document.querySelector(coverSelector);
    });
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });

And when we get bored with making arrays of all the jackets, we can access them using the jacket class:

         <div class="jacket jacket-left">
    const covers = document.querySelectorAll(".jacket");
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });

Or, by less direct methods, such as by reference from its direct parent, the video-contain element.

<div class="video-contain">
         <div class="jacket jacket-left">
    const containers = Array.from(document.querySelectorAll(".video-contain"));
    const covers = containers.map(function (videoContainer) {
        return videoContainer.firstChild;
    });
    covers.forEach(function (cover) {
        cover.addEventListener("click", coverClickHandler);
    });

There is almost always a way to deal with larger and larger groups. It’s usually best though to stay with small techniques until need demands that we use larger ones.

1 Like