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
};
});
}
}
});
2 Answers 2
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.
Comments
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()returnsNodeList, which does not havenot()method, so you have to loop over its returned HTML elements withforEach()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 addedclickin your case. This should be applied for each element in returnedNodeList- HTML element target (
document.querySelector(this.hash)) does not havelengthproperty, so you can just check that it is not false or null - I replaced jQuery's
animatewith HTML Elementanimate()(please note, that it is marked as experimental feature, so test it well), which does not have callback function, so I used theredurationproperty and setTimeout() function with the same duration. - to set focus use HTML Element
focus()method - to check if element has focus use
activeElementproperty:document.activeElement === target - to set attribute on HTML Element use
setAttribute()method
Hope this will help you to move on :)
.not(since that doesn't exist in standard DOM.