3
\$\begingroup\$

I needed to draw border element according to scroll amount. I made it work but I am not sure about optimization and performance. There may have a better way? I am open to suggestion

you can see it in action here: enter link description here

here my js:

Thanks

var el = document.querySelectorAll(".animateBorder");

document.addEventListener("scroll", (event) => {
    for (var i = 0; i < el.length; i++) {
        var mHeight = window.innerHeight / 2 || document.documentElement.clientHeight / 2;
        var currentPos = document.documentElement.scrollTop + mHeight;

        var elTop = el[i].offsetTop;
        var elHeight = el[i].offsetHeight;
        var count = el[i].dataset.count;

        var partLenght = elHeight / count;

        var elEnd = elTop + elHeight;

        var spans = el[i].getElementsByTagName("span");

        for (var s = 0; s < spans.length; s++) {
            var offset = s * partLenght;
            var _0 = elTop + offset;
            var _100 = _0 + partLenght;
            var direction = spans[s].dataset.direction;
            // console.log("s", s);
            // console.log("partLenght", partLenght);
            // console.log("elTop", elTop);
            // console.log("offset", offset);
            // console.log("currentPos", currentPos);
            // console.log("_0", _0);
            // console.log("_100", _100);
            if (_0 <= currentPos && currentPos <= _100) {
                console.log("spans[s]", s);
                console.log("direction", direction);
                if (direction == "horizontal") {
                    spans[s].style.width = Math.round(((currentPos - _0) * 100) / partLenght) + "%";
                }
                if (direction == "vertical") {
                    spans[s].style.height = Math.round(((currentPos - _0) * 100) / partLenght) + "%";
                }
            }
            if (currentPos >= _100) {
                if (direction == "horizontal") {
                    spans[s].style.width = "100%";
                }
                if (direction == "vertical") {
                    spans[s].style.height = "100%";
                }
            }

            if (currentPos <= _0) {
                if (direction == "horizontal") {
                    spans[s].style.width = "0%";
                }
                if (direction == "vertical") {
                    spans[s].style.height = "0%";
                }
            }
        }
    }
});
\$\endgroup\$
2
\$\begingroup\$

When you have a collection that you want to iterate over, and you don't particularly care about the index of each element, iterating by going through the collection's .length and manually incrementing an index counter anyway is a bit tedious and ugly. Collections almost always have iterators or a forEach method - it's nice to use those instead, when possible. In other words, rather than

var el = document.querySelectorAll(".animateBorder");
for (var i = 0; i < el.length; i++) {
  // lots of lines referencing el[i]

you may use

const borderContainers = document.querySelectorAll(".animateBorder");
for (const borderContainer of borderContainers) {
  // lots of lines referencing borderContainer

Don't call the collection el - I, and most other developers, would ordinarily expect a variable named el to contain an element. Since what you have is a collection of elements, it would be good to be precise and make that clearer with the variable name, else a script reader or writer is more likely to get confused by it and make a mistake as a result.

You define the event argument, but never use it:

document.addEventListener("scroll", (event) => {

If you're not going to use an argument, it would be better to leave it out entirely, that way there's one less variable readers of the script have to be mindful of:

document.addEventListener("scroll", () => {

scroll events can sometimes fire very frequently (like 50 in a second or two):

const div = document.querySelector('div');
window.addEventListener('scroll', () => {
  div.textContent = Number(div.textContent) + 1;
});
body {
  height: 2000px;
}
<div>0</div>

Given these sorts of events, and given that you might be iterating over a non-trivial number of animateBorder elements, and each of those animateBorder's descendant <span>s, you might be right to consider the performance implications of your code.

One way to improve would be to retrieve values only when necessary. For example, rather than doing window.innerHeight / 2 || document.documentElement.clientHeight / 2; for every scroll, and also for every animateBorder item that exists during that scroll event, you could retrieve the value just once, on pageload. You can also retrieve the scrollTop when a scroll event occurs, rather than for every animateBorder element in a scroll event. Another option would be to add a small debouncer - only iterate through the elements and change their styles once, say, 50ms has passed since the last style change has taken place.

You define

partLenght

Spelling matters in programming. Misspelled words are a very easy source of bugs. Use partLength instead.

Variables named _0 and _100 are pretty weird - it doesn't look like a normal variable name, but almost like a numeric literal with the new _ optional separators, eg:

console.log(
  1_000_000
);

Better to call the variables something easily understandable with English words instead, maybe spanTopOffset and spanBotOffset.

You have

if (direction == "horizontal") {

and a few other uses of == as well. Always use strict equality with === and !== instead - otherwise, if you use loose equality, you'll be requiring everyone that reads the code (including you) to be sure of all of the weird type coercion rules invoked by loose equality.

Since it's 2020, best to write at least in ES2015 syntax (which you're already partially doing with =>). If you're going to use ES2015 syntax, use it everywhere - never use var, since it has too many problems. Define variables with const instead (or let if you have no choice but to reassign the variable).

To clarify the logic in the comparisons against currentPos, consider using else-if and else instead, to make it clear that exactly one of the conditions may be fulfilled. I'd also add comments indicating what exactly each condition means.

Or, an alternative would be to simply calculate the percentage required regardless, then constrain its values to between 0 and 100 - then set the width or height to that constrained percentage:

// 0 when current position before or at top of span section
// between 0 and 100 when current position is between top and bottom of span section
// 100 when current position is after bottom of span section
const percentScrolledThisElement = Math.min(
  100,
  Math.max(
    0,
    Math.round(((currentPos - spanTopOffset) * 100) / partLength)
  )
);
if (direction === "horizontal") {
  span.style.width = percentScrolledThisElement + "%";
} else {
  span.style.height = percentScrolledThisElement + "%";
}

const borderContainers =  document.querySelectorAll(".animateBorder");
// If screen size changes, you can add a resize event to reassign this:
const mHeight = window.innerHeight / 2 || document.documentElement.clientHeight / 2;
document.addEventListener("scroll", () => {
  const currentPos = document.documentElement.scrollTop + mHeight;
  for (const borderContainer of borderContainers) {
		const { offsetTop, offsetHeight } = borderContainer;
    const { count } = borderContainer.dataset;
		const partLength = offsetHeight / count;
		const elEnd = offsetTop + offsetHeight;
    borderContainer.querySelectorAll('span').forEach((span, i) => {
			const offset = i * partLength;
			const spanTopOffset = offsetTop + offset;
			const spanBotOffset = spanTopOffset + partLength;
			const { direction } = span.dataset;
      
      // 0 when current position before or at top of span section
      // between 0 and 100 when current position is between top and bottom of span section
      // 100 when current position is after bottom of span section
      const percentScrolledThisElement = Math.min(
        100,
        Math.max(
          0,
          Math.round(((currentPos - spanTopOffset) * 100) / partLength)
        )
      );
      if (direction === "horizontal") {
        span.style.width = percentScrolledThisElement + "%";
      } else {
        span.style.height = percentScrolledThisElement + "%";
      }
		});
	}
});
#lipsum {
  max-width: 680px;
  margin: 0 auto;
}

.animateBorder {
  display: flex;
  outline: 1px solid red;
  height: 300px;
  width: 50%;
  align-items: center;
  justify-content: center;
  position: relative;
}

span {
  display: block;
  background-color: green;
  position: absolute;
}
span.b-top {
  top: 0;
  width: 0;
  height: 2px;
}
span.b-top.d-right {
  left: 0;
}
span.b-top.d-left {
  right: 0;
}
span.b-right {
  right: 0%;
  height: 0;
  width: 2px;
}
span.b-right.d-up {
  bottom: 0;
}
span.b-right.d-down {
  top: 0%;
}
span.b-bottom {
  top: 100%;
  width: 0;
  height: 2px;
}
span.b-bottom.d-right {
  left: 0;
}
span.b-bottom.d-left {
  right: 0;
}
span.b-left {
  left: 0%;
  height: 0;
  width: 2px;
}
span.b-left.d-up {
  bottom: 0;
}
span.b-left.d-down {
  top: 0%;
}
<div id="lipsum">
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam lacinia mi eget efficitur pretium. Aliquam neque mauris, commodo sed leo quis, rutrum rhoncus dui. Nulla in sollicitudin magna. Aliquam at fermentum metus. Integer ac diam id urna placerat ornare. Integer consequat condimentum sem, quis finibus mi hendrerit non. Morbi quis fringilla purus, consequat imperdiet mauris. Sed dictum pretium tellus, eget euismod sem.
</p>
<p>
Morbi suscipit magna nibh, vel tempus justo fermentum non. Nullam metus sapien, viverra quis viverra ac, ullamcorper ut neque. Maecenas eget orci quis libero aliquam gravida id in ligula. Mauris molestie consectetur erat, sed pharetra magna. Fusce consequat luctus libero sit amet posuere. Aliquam ultricies purus a nunc molestie elementum. Aliquam erat volutpat.
</p>
  <div class="animateBorder" data-count="3">
  <span data-direction="horizontal" class="b-top d-right"></span>
  <span data-direction="vertical" class="b-right d-down"></span>
  <span data-direction="horizontal" class="b-bottom d-left"></span>
  top,right,bottom</div>
  <div class="animateBorder" data-count="2">
  <span data-direction="vertical" class="b-left d-down"></span>
  <span data-direction="horizontal"  class="b-bottom d-right"></span>
  left,bottom</div>
<p>
Nam at ligula leo. Integer eu ante vitae nisi pellentesque feugiat in vitae neque. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante odio, placerat eu tincidunt in, luctus a diam. Aenean sed elit nunc. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur a ullamcorper orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Proin finibus elit sed urna tempus, in volutpat ipsum volutpat.
</p>
<p>
Praesent nunc elit, aliquam id sagittis in, ornare a dui. Vestibulum aliquam sem sit amet risus porttitor, ornare fringilla ex tristique. Vivamus quis augue id enim placerat finibus. In ullamcorper, tortor eu finibus euismod, sapien felis eleifend urna, feugiat convallis metus purus sed ante. Pellentesque volutpat egestas mi, in luctus mauris. Suspendisse pellentesque sed enim eu maximus. Donec vestibulum orci nunc, sed egestas mauris iaculis vel. Aenean malesuada bibendum turpis eget lobortis. Sed et ex non lorem varius interdum. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Praesent ultrices vehicula pretium. Aliquam mollis dolor ut commodo viverra. Nam nibh mauris, hendrerit et ipsum sed, aliquet auctor lacus. Maecenas tincidunt neque sed enim facilisis sagittis.
</p>
  <div class="animateBorder" data-count="3">
  <span data-direction="horizontal" class="b-top d-right"></span>
  <span data-direction="vertical" class="b-right d-down"></span>
  <span data-direction="horizontal" class="b-bottom d-left"></span>
  top,right,bottom</div>
<p>
Ut ornare, metus eu consectetur pulvinar, mi ipsum egestas nibh, et rhoncus odio nulla quis massa. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Duis tincidunt turpis nunc, vitae pretium velit condimentum non. Nunc eleifend ipsum porttitor ligula mattis faucibus. Etiam ac neque vitae nibh finibus lobortis. Morbi laoreet auctor metus, quis sagittis lectus posuere at. Aenean quis gravida lorem. Mauris aliquam, purus nec congue blandit, erat ligula ornare erat, eu malesuada augue nunc in dolor. Mauris vestibulum condimentum suscipit. Suspendisse id nibh sed ex ornare tempus vel vitae velit. Quisque sem enim, sodales nec leo et, fermentum finibus lacus. Suspendisse sit amet rutrum arcu.
</p>
<p>
Morbi nec efficitur nibh, a condimentum justo. Nunc enim nunc, gravida sed convallis eget, pulvinar nec ex. Pellentesque vitae feugiat massa. Phasellus fermentum orci eu commodo pulvinar. Ut eget porttitor purus. Nam posuere, tortor eleifend condimentum fermentum, lacus quam luctus diam, id congue tortor tortor vel nulla. Vivamus iaculis luctus mauris et dictum. Curabitur rutrum felis eros, vitae tempus risus pellentesque in.
</p>
<p>
Ut tempor feugiat efficitur. Donec cursus sem consequat malesuada malesuada. Curabitur eu turpis quis tortor aliquet lobortis. Aliquam venenatis, nibh at porttitor faucibus, ex tortor viverra elit, vitae egestas ante augue ut tellus. Phasellus venenatis finibus blandit. Suspendisse sit amet eros vitae odio iaculis accumsan. Pellentesque eu nunc dolor. Vestibulum sed massa at orci ornare bibendum.
</p>
<p>
Pellentesque nec justo ornare, iaculis turpis ut, vestibulum orci. Integer vel sem sed justo dapibus tempor. Nam hendrerit at sem eu lobortis. Duis id urna ac odio lobortis aliquet. Praesent id porttitor purus. Quisque et nibh in diam accumsan congue ac in velit. Proin libero mauris, interdum sollicitudin mauris id, dignissim ultrices metus. Etiam lacinia augue vitae suscipit euismod. Aliquam pulvinar dui semper lectus accumsan, auctor sagittis erat convallis. Donec faucibus id velit a consectetur.
</p>
<p>
Suspendisse sodales pretium molestie. Morbi vulputate posuere porta. Duis dictum ultrices facilisis. Sed lobortis libero ipsum, eget tempus turpis blandit sed. Curabitur egestas dolor metus, quis iaculis dolor mattis vitae. Donec ac accumsan leo. Aenean maximus sapien eros.
</p>
<p>
Praesent leo risus, sagittis ut sapien eget, convallis aliquam lacus. Donec posuere, mauris nec fringilla dictum, purus velit imperdiet purus, vel aliquam magna dui sit amet urna. Phasellus consequat purus id erat ornare congue quis non neque. Donec odio lectus, tincidunt eu sapien sit amet, fringilla dignissim felis. Nunc blandit vehicula ligula quis suscipit. Aenean nec auctor augue. Integer semper mauris eleifend, luctus est sit amet, hendrerit tellus. Donec at erat nec nulla convallis aliquam. Donec laoreet sit amet arcu at pretium. Sed et consequat augue, eget ultricies massa. Suspendisse luctus nulla at eros elementum fringilla.
</p>
<p>
Duis volutpat erat nec feugiat lacinia. Praesent eget consectetur arcu. Duis fringilla imperdiet libero, eget tincidunt metus tincidunt vel. Suspendisse dignissim quis velit a tempor. Curabitur finibus nec tellus ac aliquet. Cras porttitor efficitur tristique. Maecenas bibendum lacus dui, in mattis tortor aliquam vel. Vestibulum quis tortor semper, consequat nulla eget, lacinia ante. Phasellus ornare vel massa nec eleifend. In aliquet ultricies augue.
</p>
<p>
Nam ante augue, condimentum non nulla in, imperdiet dignissim augue. Curabitur sodales ullamcorper turpis, vitae convallis lacus vulputate eget. In mollis ipsum eleifend porttitor egestas. Aenean orci mi, scelerisque euismod justo id, luctus hendrerit tortor. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In lobortis vitae dui ac efficitur. Nullam iaculis vulputate mi, sed pharetra turpis ultrices non. Pellentesque vitae sodales orci, non vulputate odio. Aliquam eu molestie ex. Maecenas pellentesque lorem est, quis lacinia odio tincidunt nec. Suspendisse pharetra est et magna tincidunt scelerisque.
</p>
<p>
Vestibulum eu finibus orci. Sed non egestas felis. Vestibulum et nisi vel metus faucibus mattis. Vivamus at ligula sodales, commodo nisl vitae, pharetra tellus. Phasellus quis magna blandit, suscipit magna ac, interdum sem. Cras facilisis dui eu dolor efficitur, sed commodo velit placerat. Sed tempus tortor sit amet nunc dignissim, sit amet vestibulum mi luctus. Donec eget elit ex. Aenean sodales viverra est. Sed accumsan mauris turpis, eu rhoncus lacus volutpat eu. Curabitur et consectetur felis. Nullam at nulla vestibulum, eleifend quam ac, tristique odio. Phasellus sapien ante, blandit ultricies eros at, semper viverra elit.
</p>
<p>
Sed dictum accumsan efficitur. Sed convallis lobortis porttitor. Nullam vitae lorem sed felis semper tincidunt. Mauris ut ipsum purus. Cras ultricies pharetra tellus sit amet accumsan. Quisque sed nulla lorem. Aliquam pulvinar a nisi sit amet semper. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla aliquam scelerisque erat et facilisis.
</p>
<p>
Cras ac varius odio, in sollicitudin sapien. Nunc tincidunt vulputate tortor ac euismod. Phasellus dignissim et orci sit amet malesuada. Sed suscipit lectus libero. Maecenas ac aliquam diam, sit amet feugiat nibh. Duis vehicula sem nec tincidunt viverra. Donec non dolor nulla. Curabitur iaculis justo vitae vehicula egestas. Phasellus efficitur volutpat faucibus. Morbi ligula nisl, porttitor nec lectus non, accumsan ornare augue. Nullam imperdiet nisl eu urna bibendum finibus nec vel augue. Phasellus vestibulum dui at interdum sollicitudin. Etiam ac commodo metus. Vestibulum augue metus, commodo vitae felis venenatis, aliquet feugiat mauris. Duis consequat dui enim, in posuere lorem aliquet vitae.
</p></div>

\$\endgroup\$
  • \$\begingroup\$ Thank A LOT! There is a miscalculation of partLength it should be const partLength = offsetHeight / count; instead of const partLength = offsetTop / count; \$\endgroup\$ – a_prods May 20 '20 at 16:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.