DEV Community

Cover image for Do you need a selector engine like jQuery, Sizzle or something else?
Frank Wisniewski
Frank Wisniewski

Posted on

Do you need a selector engine like jQuery, Sizzle or something else?

the following example:

create a ScrollToTop button in vanilla - old school style

"use strict";

  document.addEventListener( 'DOMContentLoaded', function () {

    const scrollButton = document
      .createElementNS( 'http://www.w3.org/2000/svg', 'svg' );

      scrollButton.setAttributeNS( 'http://www.w3.org/2000/xmlns/', 
        'xmlns:xlink', 'http://www.w3.org/1999/xlink' );
      scrollButton.setAttribute( 'viewBox', "0 0 100 100" );

      scrollButton.style.width ='50px'
      scrollButton.style.position = 'fixed'
      scrollButton.style.right = '10px'
      scrollButton.style.bottom = '10px'
      scrollButton.style.zIndex = '1000'
      scrollButton.style.opacity = '0.5'
      scrollButton.style.cursor = 'pointer'
      scrollButton.style.transition = 'all ease 400ms'
      scrollButton.style.display = 'none'

    const myCircle = document
      .createElementNS( 'http://www.w3.org/2000/svg', 'circle' );
      myCircle.setAttribute( 'cx', '50' );
      myCircle.setAttribute( 'cy', '50' );
      myCircle.setAttribute( 'r', '50' );
      myCircle.setAttribute( 'fill', 'red' );

    scrollButton.appendChild( myCircle );

    const myArrow = document
      .createElementNS( 'http://www.w3.org/2000/svg', 'path' );

      myArrow.setAttribute( 'd', 'M50 80 L50 20 M50 20 L80 50 M50 20 L20 50' );
      myArrow.setAttribute( 'stroke', 'white' );
      myArrow.setAttribute( 'stroke-width', '15' );
      myArrow.setAttribute( 'stroke-linecap', 'round' );

    scrollButton.appendChild( myArrow );

    scrollButton.addEventListener( 'mouseover', 
      () => scrollButton.style.opacity = '1'
    );

    scrollButton.addEventListener( 'mouseout', 
      () => scrollButton.style.opacity = '.3'
    );

    scrollButton.addEventListener( 'click',  () => document
      .querySelector( 'body' )
      .scrollIntoView( { behavior: 'smooth' } )
    );

    document.body.appendChild( scrollButton );

    window.onscroll = () =>
      scrollButton.style.display =
          ( document.querySelector( 'html' ).scrollTop / 
           ( document.querySelector( 'html' ).scrollHeight - 
            document.querySelector( 'html' ).clientHeight ) ) > 0.25
        ? '' : 'none';

  }) 
Enter fullscreen mode Exit fullscreen mode

now the same solution using a selector engine.
In this example I used my own engine, it doesn't matter for the comparison.

<script type="text/javascript" src="https://w-web.de/myquery/myquery-min.js"></script>

or

<script type="text/javascript" src="https://w-web.de/myquery/myquery.js"></script>

"use strict";
$().ready( () => {

    let scrollButton = 
      $().sCreate( '0 0 100 100' )

      .styles()
        .width(  '50px' ).position( 'fixed' )
        .right( '10px' ).bottom( '10px' )
        .zIndex( '1000' ).opacity( '0.3' )
        .cursor( 'pointer' ).transition( 'all ease 400ms' )
        .display( 'none' ).parent()

      .sAppend( 
        $().sCircle( 50, 50, 50 ).fill( 'red' ) )

      .sAppend(
        $().sPath( 'M50 80 L50 20 M50 20 L80 50 M50 20 L20 50' )
          .shortStroke( 'white' , '15', 'round' ) )

      .on( 'mouseover', function (){ this.style.opacity = '1' } )

      .on( 'mouseout', function (){ this.style.opacity = '.3' } )

      .click( () => $( 'body' ).n.scrollIntoView( { behavior: 'smooth' } ) )

      .appendEnd( $( 'body' ) )

    $(window).on('scroll', () => 
        scrollButton.n.style.display =
          ( $('html').n.scrollTop / 
           ( $('html').n.scrollHeight - 
             $('html').n.clientHeight ) ) > 0.25
        ? '' : 'none'
    ) 
  })
Enter fullscreen mode Exit fullscreen mode

Advantages of a SelectorEngine

  • shorter code
  • Chaining

Disadvantages

  • greater learning effort

I think that if you have to deal with a lot of changing employees in a team, the vanilla JS solution certainly makes more sense.

If you are a lone fighter, you should use a Selector Engine.

what is your opinion?

Discussion (3)

Collapse
lukeshiru profile image
Luke Shiru

If the code becomes complex, is generally a sign that the code should be updated, not that we need to add a new dependency like jQuery. This particular problem has several solutions that are way simpler, here's one for example:

The styles are moved to CSS, making it way easier to customize and less complex on the JS side. No need for an "onload", you just defer it. No need for event listeners for style related changes, just use CSS. No need to create a bunch of static elements, when we can just use a template (ideally even a SVG file).

Here's the JS of that CodePen:

const { body, documentElement } = document;

const template = Object.assign(document.createElement("template"), {
    innerHTML: `
        <svg class="scroll-button" hidden viewBox="0 0 100 100">
            <circle />
            <path d="M50 80 L50 20 M50 20 L80 50 M50 20 L20 50" />
        </svg>
    `,
});

const scrollButton = template.content.cloneNode(true).querySelector("svg");

scrollButton.addEventListener("click", () =>
    body.scrollIntoView({ behavior: "smooth" }),
);

body.appendChild(scrollButton);

window.addEventListener("scroll", () =>
    scrollButton[
        documentElement.scrollTop > 100 ? "removeAttribute" : "setAttribute"
    ]("hidden", ""),
);
Enter fullscreen mode Exit fullscreen mode

Even if you still think the jQuery solution is more readable somehow, you still need to consider those 30.3kB (Minified and Gzipped) that you're adding to your app.

Cheers!

Collapse
frankwisniewski profile image
Frank Wisniewski Author • Edited on

There are many ways to Rome, of course I know that this can also be solved in other ways.

As you may have overlooked, the code was designed as a plugin. That's why the ready, scope!!

It's not about a CodeGolf competition.

In my opinion, your code is unnecessarily complicated, why use template?

Why the strange way via the hidden attribute, which is ultimately misused as a class?

Why assign the circle attribute in the CSS?

All of this means that the code shown does not work on IOS devices, for example!!

sample code, if you don't design it as a plugin:

css

.hidden {
  display: none;
}

.scroll-button {
  bottom: 10px;
  cursor: pointer;
  opacity: 0.5;
  position: fixed;
  right: 10px;
  transition: all ease 400ms;
  width: 50px;
  z-index: 1000;
}

.scroll-button:hover {
  opacity: 1;
}

.scroll-button circle {
    fill: red;
  }

.scroll-button path {
  stroke: #fff;
  stroke-width: 15;
  stroke-linecap: round;
}
Enter fullscreen mode Exit fullscreen mode

Javascript:

  document.body.insertAdjacentHTML( 'beforeend', 
      `<svg xmlns:xlink="http://www.w3.org/1999/xlink" 
        id = scrollButton class="scroll-button hidden" 
         viewBox="0 0 100 100">
        <circle cx="50" cy="50" r="50"></circle>
        <path d="M50 80 L50 20 M50 20 L80 50 M50 20 L20 50" />
      </svg>` )

  scrollButton.onclick=() =>
    document.body.scrollIntoView( { behavior: "smooth" } )

  window.addEventListener( 'scroll', () => 
    scrollButton.classList.toggle('hidden', 
    document.documentElement.scrollTop < 100 ))
Enter fullscreen mode Exit fullscreen mode
Collapse
lukeshiru profile image
Luke Shiru • Edited on

There are many ways to Rome, of course I know that this can also be solved in other ways.

I agree. My point was mainly that jQuery is not the best way to Rome, but you can use it if you want. I'm not saying mine is the best either, but gets the same job done without the need of jQuery.

As you may have overlooked, the code was designed as a plugin. That's why the ready, scope!!

I get is a plugin, but if you defer it, it has the same effect (both for jQuery and vanilla):

<script src="./scroll-top.js" defer></script>
Enter fullscreen mode Exit fullscreen mode

That will run the script when the DOM is ready. And you can just load your styles with that:

<script src="./scroll-top.js" defer></script>
<link rel="stylesheet" href="./scroll-top.css" />
Enter fullscreen mode Exit fullscreen mode

It's not about a CodeGolf competition.

I wasn't golfing. Trust me this is far from being golf.

In my opinion, your code is unnecessarily complicated, why use template?

Because the template element is used to ... well ... create templates. And if you just want to have an SVG with some elements on it that will never change or have events on their own, then a template is just fine. You could also just use a div in memory, but I think template is a better element for that because it is designed with this kind of usages in mind.

Why the strange way via the hidden attribute, which is ultimately misused as a class?

Is not a "strange way", is actually a standard global attribute. Ideally you should use it every time you want to "hide" something, is also better for a11y.

Why assign the circle attribute in the CSS?

TBH, I almost removed the circle altogether and replaced it with a button with CSS to make it look like a circle (which is also better for a11y than a floating SVG). But I was already doing a lot of changes just to showcase that you don't need jQuery for this, so it felt like an overkill already. So I just moved the styles to whey they belong (CSS).

All of this means that the code shown does not work on IOS devices, for example!!

Safari is way behind the curve in a lot of stuff, but yeah, I didn't even check Safari for the same reasons above. If you really want me to create an example that works on iOS and still demonstrates that you don't need jQuery, I'd be happy to do it, but maybe the point is already made without that.

sample code, if you don't design it as a plugin

Why didn't you had that code as an example for JS? Is way better than the one on the article and still works as a "plugin" (you just put that code on a deferred script tag and done). It still needs some work (missing hidden attribute, unnecessary xmlns:xlink attribute, still not inside a button, usage of id, and so on), but it better showcases how vanilla nowadays is so good you generally don't even need jQuery.