0

I am trying to push my JS knowledge, hence I am trying to convert JQuery code to JavaScript. This is jQuery code

      $('a[href*="#"]')// Select all links with hashes
              // Remove links that don't actually link to anything
              .not('[href="#"]')
              .not('[href="#0"]')
              .click(function(event) {
                // On-page links
                if (
                  location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') 
                  && 
                  location.hostname == this.hostname
                ) {
                  // Figure out element to scroll to
                  var target = $(this.hash);
                  target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
                  // Does a scroll target exist?
                  if (target.length) {
                    // Only prevent default if animation is actually going to happen
                    event.preventDefault();
                    $('html, body').animate({
                      scrollTop: target.offset().top
                    }, 850, function() {
                      // Callback after animation
                      // Must change focus!
                      var $target = $(target);
                      $target.focus();
                      if ($target.is(":focus")) { // Checking if the target was focused
                        return false;
                      } else {
                        $target.attr('tabindex','-1'); // Adding tabindex for elements not focusable
                        $target.focus(); // Set focus again
                      };
                    });
                  }
                }
              });

And this is something that I have changed, but I'm not sure how to continue, I'm a beginner so I am facing some struggles.I try my level best to convert that JQuery code to "vanilla" JS but did some mistakes. Can you please tell me where I went wrong??

  document.querySelectorAll('a[href*="#"]')// Select all links with hashes
  // Remove links that don't actually link to anything
  .not('[href="#"]')
  .not('[href="#0"]')
  .addEventListener(function(event) {
    // On-page links
    if (
      location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') 
      && 
      location.hostname == this.hostname
    ) {
      // Figure out element to scroll to
      var target =document.querySelector(this.hash);
      target = target.length ? target : $('[name=' + this.hash.slice(1) + ']');
      // Does a scroll target exist?
      if (target.length) {
        // Only prevent default if animation is actually going to happen
        event.preventDefault();
        $('html, body').animate({
          scrollTop: target.offset().top
        }, 850, function() {
          // Callback after animation
          // Must change focus!
          var $target = $(target);
          $target.focus();
          if ($target.is(":focus")) { // Checking if the target was focused
            return false;
          } else {
            $target.attr('tabindex','-1'); // Adding tabindex for elements not focusable
            $target.focus(); // Set focus again
          };
        });
      }
    }
  });
4
  • You should look at your console and look for errors. I expect it's having a problem with .not( since that doesn't exist in standard DOM. Commented Jun 1, 2020 at 13:35
  • welcome, 1) you still have some jquery calls in the second posting. 2) break these into smaller functions that do 1 thing so they are more testable. this will make it easier for you to isolate your incorrect syntax 3) include all pertinent code ideally in a snippet so that we can see how it should function before unwiring the jquery Commented Jun 1, 2020 at 13:37
  • " pertinent code ideally " You mean to include, like CSS and Html code or..? @ts Commented Jun 1, 2020 at 13:38
  • youmightnotneedjquery.com github.com/nefe/You-Dont-Need-jQuery Commented Jun 1, 2020 at 13:40

2 Answers 2

1

As your goal is to learn, I'll try to help with next steps rather than give a code solution.

jQuery uses fluent API. This is the syntax allowing you to chain operations, one after another. For example, see the way .not and .click are chained here:

$('a[href*="#"]').not('[href="#"]').not('[href="#0"]').click ...

Although fluent interfaces can be created in Javascript, the language itself does not have a fluent API. The converted code would initially fail due to this foundational difference.

To convert the jQuery to Javascript, one approach is to look for functional parts of the code and then wrap those in functions. At this point it could still be jQuery in the functions. An example of this would be the code that selects the correct links on the page.

Next, you could call each function one by one, learning about and converting the parts which fail. You've correctly identified for example that one difference is the way selectors work.

A less frustrating approach might be to start from scratch though, after identifying the goal of the code overall. Snippets can still be taken from the jQuery version, but it could be easier to troubleshoot raising it from the ground yourself. This route might even mean ending up with simpler code for your intended purpose.

Sign up to request clarification or add additional context in comments.

Comments

0

you can try something like:

// Select all links with hashes excluding links that don't actually link to anything
const selector = 'a[href*="#"]:not([href="#"]):not([href="#0"])';
document.querySelectorAll(selector).forEach(el => {
  el.addEventListener('click', event => {
    // On-page links
    const cond1 = location.pathname.replace(/^\//, '') == el.pathname.replace(/^\//, '');
    const cond2 = location.pathname == el.pethname;
    if (cond1 && cond2) {
      // Figure out element to scroll to
      let target = document.querySelector(el.hash);
      target = target ? target : document.querySelector(`[name="${el.hash.slice(1)}"]`);
      // Does a scroll target exist?
      if (target) {
        // Only prevent default if animation is actually going to happen
        event.preventDefault();

        document.body.animate({
          scrollTop: target.offsetTop
        }, {
          duration: 850
        });

        setTimeout(() => {
          // Callback after animation. Must change focus!
          target.focus();
          if (document.activeElement === target) {
            return false;
          }
          else {
            // Adding tabindex for elements not focusable
            target.setAttribute('tabindex', '-1');
            // Set focus again
            target.focus();
          }
        }, 850);

      }
    }
  });
  console.log(el.pathname, el.animatee);
});

I did not check the logic of your code and did not test my code, but I hope, it can give you a starting point...

now regarding

where I went wrong??

in your code:

  • querySelectorAll() returns NodeList, which does not have not() method, so you have to loop over its returned HTML elements with forEach() method. In your example you can achieve same result only by CSS selector: a[href*="#"]:not([href="#"]):not([href="#0"]) (using CSS pseudo class :not)
  • addEventListener()'s first argument should be a string representing event type, I added click in your case. This should be applied for each element in returned NodeList
  • HTML element target (document.querySelector(this.hash)) does not have length property, so you can just check that it is not false or null
  • I replaced jQuery's animate with HTML Element animate() (please note, that it is marked as experimental feature, so test it well), which does not have callback function, so I used there duration property and setTimeout() function with the same duration.
  • to set focus use HTML Element focus() method
  • to check if element has focus use activeElement property: document.activeElement === target
  • to set attribute on HTML Element use setAttribute() method

Hope this will help you to move on :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.