Next up on the conversion is the append commands.
Append
JavaScript appends starting with the parent, and instructing what to append. To help improve the success of our conversion, we should first change appendTo to append, and switching around the terms.
$("#more").appendTo("body");
$("body").append("#more");
To help with simplicity, we can move that more element out to a separate variable:
const more = $("#more");
...
$("body").append(more);
We can now convert those jQuery references to standard JavaScript ones instead:
const more = document.querySelector("#more");
...
document.querySelector("body").appendChild(more);
Replace menu_ul
Currently #menu_ul is used a lot in the jQuery code. We can easily assign references to the menu as variables and get it from there instead:
const menu = document.querySelector(".menu");
const menuList = document.querySelector("#menu_ul");
We can now replace occurrences of #menu_ul in the code with menuList instead:
// var item_width = $('#menu_ul li').width();
var item_width = $("li", menuList).width();
// var item_count = ($("#menu_ul li").length);
var item_count = $("li", menuList).length;
// var nav_width_og = $(".menu").width();
var nav_width_og = $(menu).width();
// var nav_width = $(".menu").width();
var nav_width = $(menu).width();
We can do similar replacements throughout the code, so that string references are all replaced with variable references instead. That’s going to help to make the conversion a lot easier.
Replace not()
The code that uses .not()
can now be updated to use the :not()
pseudo-selector instead:
// $('.overflow').append($("li", menuList).not("#more").last());
$('.overflow').append($("li:not(#more)", menuList).last());
Replace last()
Even though CSS selectors have the :last-child pseudo selector, we can’t use that here as the :not() parts interfere with things.
Instead, we can use a getLastMenuItem function:
function getLastItem(listGroup) {
return $("li:not(#more)", listGroup).last();
}
...
const lastMenuItem = getLastItem(menuList);
$('.overflow').append(lastMenuItem);
That way, we can convert the functions separately from the rest of the code.
Before we convert the lastMenuItem function, we need the lastGroup parameter to not be a jQuery object.
function getLastItem(listGroup) {
if (listGroup instanceof jQuery) {
throw new Error("jQuery object found");
}
return $("li:not(#more)", listGroup).last();
}
A jQuery object was found in the form of the overflow element. Updating that results in no more errors being thrown.
if (nav_width > (item_width * item_count) + (item_width - 1)) {
// const lastOverflowItem = getLastItem($(".overflow");
const overflow = menu.querySelector(".overflow");
const lastOverflowItem = getLastItem(overflow);
Now that the getLastItem function is being given elements instead of jQuery objects, we can remove jQuery from the getLastItem function too.
function getLastItem(listGroup) {
const items = listGroup.querySelectorAll("li:not(#more)");
return items[items.length - 1];
}
Replacing append
Now that the parts being used with append are normal JavaScript objects, we can remove the jQuery append. This is also a good time to move the overflow variable to the top of the code too, to go along with the menu ones.
const menu = document.querySelector(".menu");
const menuList = menu.querySelector("#menu_ul");
const overflow = menu.querySelector(".overflow");
...
// $(menuList).append(lastOverflowItem);
menuList.appendChild(lastOverflowItem);
Replace width and length
There’s only one other jQuery aspect that needs to be replaced now, and that’s getting the width and number of items.
// nav_width = $(menu).width;
nav_width = menu.clientWidth;
// item_width = $("li", menuList).width();
const item_width = menuList.querySelector("li").clientWidth;
// item_count = $("li", menuList).length;
const item_count = menuList.querySelectorAll("li").length;
Remove the jQuery library
That’s removed all of the jQuery from the code, so we don’t need the jQuery library anymore. In the jsFiddle resources section I can now remove jQuery.
Tidying things up
There’s only one significant issue remaining and that’s when the overflow code tries to add a non-existing item to the menu. A good way to deal with that is to move the code in the if statement out to a separate function.
function moveToOverflow(item) {
overflow.appendChild(item);
menuList.appendChild(more);
more.classList.remove("hide");
}
if (nav_width < (item_width * item_count)) {
// const lastMenuItem = getLastItem(menuList);
// overflow.appendChild(lastMenuItem);
// menuList.appendChild(more);
// more.classList.remove("hide");
moveToOverflow(getLastItem(menuList));
}
We can put the same protection in place with moveToMenu too:
function moveToMenu(item) {
if (!item) {
return;
}
menuList.appendChild(item);
menuList.appendChild(more);
}
...
if (nav_width > (item_width * item_count) + (item_width - 1)) {
moveToMenu(getLastItem(overflow));
}
And the code is now robust JavaScript code that does the same job.
Summary
Here’s the full JavaScript code:
function getLastItem(listGroup) {
const items = listGroup.querySelectorAll("li:not(#more)");
return items[items.length - 1];
}
function moveToOverflow(item) {
if (!item) {
return;
}
overflow.appendChild(item);
menuList.appendChild(more);
more.classList.remove("hide");
}
function moveToMenu(item) {
if (!item) {
return;
}
menuList.appendChild(item);
menuList.appendChild(more);
}
const menu = document.querySelector(".menu");
const menuList = menu.querySelector("#menu_ul");
const overflow = menu.querySelector(".overflow");
const item_width = menuList.querySelector("li").clientWidth;
const item_count = menuList.querySelectorAll("li").length;
const nav_width_og = menu.clientWidth;
const nav_width = menu.clientWidth;
const more = document.querySelector("#more");
if ((item_width * (item_count + 1)) > nav_width) {
document.querySelector('body').appendChild(more);
more.classList.add("hide");
}
for (var i = 0; i < item_count; i++) {
const nav_width = menu.clientWidth;
const item_width = menuList.querySelector("li", menuList).clientWidth;
const item_count = menuList.querySelectorAll("li").length;
if (nav_width < (item_width * item_count)) {
moveToOverflow(getLastItem(menuList));
}
}
window.addEventListener("resize", function() {
const nav_width = menu.clientWidth;
const item_width = menuList.querySelector("li").clientWidth;
const item_count = menuList.querySelectorAll("li").length;
if (nav_width < (item_width * item_count)) {
moveToOverflow(getLastItem(menuList));
}
if (nav_width > (item_width * item_count) + (item_width - 1)) {
moveToMenu(getLastItem(overflow));
}
if (nav_width == nav_width_og) {
document.querySelector("body").appendChild(more);
more.classList.add("hide");
}
});
more.addEventListener("click", function() {
overflow.classList.toggle("toggled");
});
And the code can be explored at https://jsfiddle.net/y8j710dh/