Three-layer parallax effect I want to make it lightweight (fastest, best performance) as it possible for browser, but I have no idea - I haven't huge knowledge about JavaScript.

That code run parallax effect on my web page. I have three parallax containers, which are different position background image. Script call on scroll event and when some container exist it's making it parallax-y. The first container ('.parallax-top') exists always and hasn't any offset of image - it is always top-positioned. Second and third have offsets (1700 and 1400). 

My page have a lot of content, scripts, images and I want to make all of them lightweight as it is possible.

if ($(window).width() &gt; 1200) {
    (function() {
        var parallaxTop = document.querySelectorAll(".parallax-top");
        if ($(".parallax-center").length) {
            var parallaxCenter = document.querySelectorAll(".parallax-center");
            var centerPosition = $(".parallax-center").position().top;
        }

        if ($(".parallax-bottom").length) {
            var parallaxBottom = document.querySelectorAll(".parallax-bottom");
            var bottomPosition = $(".parallax-bottom").position().top;
        }
        var speed = 0.4;
        window.onscroll = function() {

            [].slice.call(parallaxTop).forEach(function(el, i) {
                var windowYOffset = window.pageYOffset,
                    elBackgrounPos = "center " + "-" + (windowYOffset * speed) + "px";
                el.style.backgroundPosition = elBackgrounPos;
            });
            if ($(".parallax-center").length)
                [].slice.call(parallaxCenter).forEach(function(el, i) {
                    var windowYOffset = window.pageYOffset,
                        elBackgrounPos = "center " + "" + ((windowYOffset - centerPosition) * speed + 1700) + "px";
                    el.style.backgroundPosition = elBackgrounPos;
                });
            if ($(".parallax-bottom").length)
                [].slice.call(parallaxBottom).forEach(function(el, i) {
                    var windowYOffset = window.pageYOffset,
                        elBackgrounPos = "center " + "" + ((windowYOffset - bottomPosition) * speed + 1400) + "px";
                    el.style.backgroundPosition = elBackgrounPos;
                });
        };
    })();
}
First, why are you using querySelectorAll when you use jQuery? Use jQuery's $(SELECTOR_STRING_HERE) instead.
I believe you're using querySelectorAll because you're trying to use forEach. Unfortunately, forEach is slower than your regular loops. Internally, jQuery uses a for-in and for in their each implementation, which may be faster than forEach.
Again, you use jQuery but you use window.onscroll. Use jQuery's event mechanism instead.


I believe you're trying to go native in your code assuming things would go faster. But you're trying to solve the wrong problem. The real problem is the scroll event. Just scrolling a notch of your mouse wheel will fire the scroll event a dozen times, more or less. Now in your handler, you have this:

[].slice.call(parallaxTop)


What this code does is:


Create an array (the [] part)
Create a copy of that array (the call to slice)
It appears 3 times in your handler


That means if the handler fired a dozen times, this means you created 6 arrays per fire. A dozen would mean 72 arrays. Note that when the garbage collector runs, it "stops the world" (stops everything, scripting and rendering), causing freezes every now and then.

Additionally, forEach is synchronous. Nothing else happens when it's running, and doing something that takes too long inside each iteration will hurt performance. That and the GC, you'll eventually leave a very small gap for rendering.

What I suggest is to move your logic in a separate function and throttle your handler. That is, you only call your logic once the scroll has fired for at least some time, or at least a number of times. Only then will you update your element positions.

Now you'd wonder if you scrolled for a second and you scrolled 500px, wouldn't the background just jump? Yes, but that's where CSS transitions come in. You transition the background-position property so that the background will slide (depending on the easing function) to the updated position instead of jumping to it.

But, if I were you, I'd save my time and effort and instead, use a plugin for parallax effects.