Wrap n-th or less element, wrong result - javascript

I have drop down menu in wordpress and I have small problem with wrap nth(every 4th element) li.
My code
<li id="item1"> ... </li>
<li id="item2"> ... </li>
<li id="item3">
<ul class="sub-menu">
<li> Test1</li>
<li> Test2</li>
</ul>
</li>
<li id="item4">
<ul class="sub-menu">
<li> Test3</li>
<li> Test4</li>
<li> Test5</li>
<li> Test6</li>
<li> Test7</li>
<li> Test8</li>
<li> Test9</li>
<li> Test10</li>
<li> Test11</li>
<li> Test12</li>
</ul>
</li>
jQuery code
var divs = $("ul.sub-menu > li");
for(var i = 0; i < divs.length; i+=4) {
divs.slice(i, i+4).wrapAll("<div class='column'></div>");
}
My results (wrong)
<li id="item1"> ... </li>
<li id="item2"> ... </li>
<li id="item3">
<ul class="sub-menu">
<div class="column">
<li> Test1</li>
<li> Test2</li>
<li> Test3</li>
<li> Test4</li>
</div>
</ul>
</li>
<li id="item4">
<ul class="sub-menu">
<div class="column">
<li> Test5</li>
<li> Test6</li>
<li> Test7</li>
<li> Test8</li>
</div>
<div class="column">
<li> Test9</li>
<li> Test10</li>
<li> Test11</li>
<li> Test12</li>
</div>
</ul>
</li>
What I try achieve:
<li id="item1"> ... </li>
<li id="item2"> ... </li>
<li id="item3">
<ul class="sub-menu">
<div class="column">
<li> Test1</li>
<li> Test2</li>
</div>
</ul>
</li>
<li id="item4">
<ul class="sub-menu">
<div class="column">
<li> Test3</li>
<li> Test4</li>
<li> Test5</li>
<li> Test6</li>
</div>
<div class="column">
<li> Test7</li>
<li> Test8</li>
<li> Test9</li>
<li> Test10</li>
</div>
<div class="column">
<li> Test11</li>
<li> Test12</li>
</div>
</ul>
</li>
any idea what I do wrong? It's necessary because "item3" and "item4" are diffrents category and with my code li elements are mix if in first ul is less then 4 items

Wow, what you just did, I didn't know it was possible. :)
Anyway:
$('.sub-menu').each(function() {
var children = $(this).find('> li');
for (var i = 0; i < children.length; i+=4) {
children.slice(i, i+4).wrapAll("<div class='column'></div>");
}
});
This will use the functionality you tried, but it will loop through every submenu independently and the items will be properly grouped.
http://jsfiddle.net/9NUwJ/

Related

how to make clickable buttons that add text into a div

i want to create a function that adds text into an empty div depending on what button is clicked
i'm making a calculator as one of my beginner javascript projects (i know i can just use inputs and the "button" tag but i feel this'll be more original and challenging). i'm trying to add events to all the elements in the queryselector all nodelist, i feel my function is correct and i've tried all i can but the code doesn't seem to work, any help at all would be appreciated.
i am trying to create a kind of like input div that reflects the value or text of the clicked list item
let digits = document.querySelectorAll("digit");
let add = document.getElementById("camper");
for (let i = 0; i < digits.length; i++) {
digits[i].addEventListener("click", function() {
add.innerHTML += digits[i].innerHTML
});
digits[i].value = [i];
};
<div class="container">
<div class="camper">
<h3 id="insert"></h3>
</div>
<div class="right-box">
<ul>
<li>
<h3 id="equal">=</h3>
</li>
<li>
<h3 id="plus">+</h3>
</li>
<li>
<h3 id="minus">-</h3>
</li>
<li>
<h3 id="multiply">x</h3>
</li>
<li>
<h3 id="divide">\</h3>
</li>
</ul>
</div>
<div class="write-box">
<ul>
<li class="digit">
<h3>1</h3>
</li>
<li class="digit">
<h3>2</h3>
</li>
<li class="digit">
<h3>3</h3>
</li>
<li class="digit">
<h3>4</h3>
</li>
<li class="digit">
<h3>5</h3>
</li>
<li class="digit">
<h3>6</h3>
</li>
<li class="digit">
<h3>7</h3>
</li>
<li class="digit">
<h3>8</h3>
</li>
<li class="digit">
<h3>9</h3>
</li>
<li class="digit">
<h3>0</h3>
</li>
<li>
<h3>clear</h3>
</li>
<li>
<h3>bkspc</h3>
</li>
</ul>
</div>
</div>
Typos
it is id="camper" and not class="camper" if you want to use getElementById but you likely want insert instead of camper
it is querySelectorAll(".digit") - you are missing the dot in the class selector
Logic
use this.innerText to get the content of the digit
let digits = document.querySelectorAll(".digit");
let add = document.getElementById("insert");
for (let i = 0; i < digits.length; i++) {
digits[i].addEventListener("click", function() {
add.innerHTML += this.innerText;
});
};
<div class="container">
<div class="camper">
<h3 id="insert"></h3>
</div>
<div class="right-box">
<ul>
<li>
<h3 id="equal">=</h3>
</li>
<li>
<h3 id="plus">+</h3>
</li>
<li>
<h3 id="minus">-</h3>
</li>
<li>
<h3 id="multiply">x</h3>
</li>
<li>
<h3 id="divide">\</h3>
</li>
</ul>
</div>
<div class="write-box">
<ul>
<li class="digit">
<h3>1</h3>
</li>
<li class="digit">
<h3>2</h3>
</li>
<li class="digit">
<h3>3</h3>
</li>
<li class="digit">
<h3>4</h3>
</li>
<li class="digit">
<h3>5</h3>
</li>
<li class="digit">
<h3>6</h3>
</li>
<li class="digit">
<h3>7</h3>
</li>
<li class="digit">
<h3>8</h3>
</li>
<li class="digit">
<h3>9</h3>
</li>
<li class="digit">
<h3>0</h3>
</li>
<li>
<h3>clear</h3>
</li>
<li>
<h3>bkspc</h3>
</li>
</ul>
</div>
</div>
Here is a more elegant way - if you are doing homework, you might want to do more coding before looking :)
let add = document.getElementById("insert");
document.getElementById("container").addEventListener("click",function(e) {
const tgt = e.target;
if (tgt.tagName==="H3") {
const text = tgt.innerText;
const cn = tgt.parentNode.className;
if (cn === "digit" || cn === "oper") add.innerText += text;
else if (cn==="action") {
if (text === "clear") {
add.innerText = "";
}
else if (text === "bkspc") {
add.innerText = add.innerText.slice(0,-1);
}
}
}
})
<div id="container">
<div class="camper">
<h2 id="insert"></h2>
</div>
<div class="right-box">
<ul>
<li class="oper">
<h3 id="equal">=</h3>
</li>
<li class="oper">
<h3 id="plus">+</h3>
</li>
<li class="oper">
<h3 id="minus">-</h3>
</li>
<li class="oper">
<h3 id="multiply">x</h3>
</li>
<li class="oper">
<h3 id="divide">\</h3>
</li>
</ul>
</div>
<div class="write-box">
<ul>
<li class="digit">
<h3>1</h3>
</li>
<li class="digit">
<h3>2</h3>
</li>
<li class="digit">
<h3>3</h3>
</li>
<li class="digit">
<h3>4</h3>
</li>
<li class="digit">
<h3>5</h3>
</li>
<li class="digit">
<h3>6</h3>
</li>
<li class="digit">
<h3>7</h3>
</li>
<li class="digit">
<h3>8</h3>
</li>
<li class="digit">
<h3>9</h3>
</li>
<li class="digit">
<h3>0</h3>
</li>
<li class="action">
<h3>clear</h3>
</li>
<li class="action">
<h3>bkspc</h3>
</li>
</ul>
</div>
</div>
You are losing the scope of digits inside the event listener. You need to bind digits to be the scope of this in the event listener like so:
let digits = document.querySelectorAll("digit");
let add = document.getElementById("camper");
for (let i = 0; i < digits.length; i++) {
digits[i].addEventListener("click", function() {
add.innerHTML += this.innerHTML
}).bind(digits[i]);
digits[i].value = [i];
};

How to create dynamic menu with list items?

I am trying to create a menu using list item's and unordered list's that are nested within each other to display sub menus. When each list item is clicked, it will display the child unordered list by adding the "show" class. I can't seem to figure out how to remove the "show" class once one of the "topLevel" list item components is clicked (those are the three main menu options).
Here's the jsFiddle: https://jsfiddle.net/eu1rc4ma/2/
Thanks in advance!
Tried using spans to remove the class from all child components.
<div class="container">
<div class="menu">
<ul class="show">
<li>
<span class="topLevel">Services</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
<li>
<span class="topLevel">Technology</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
<li>
<span class="topLevel">About</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
ul {
display: none;
}
.show {
display: flex;
flex-direction: column;
}
let targets = document.querySelectorAll("li");
targets.forEach(function(target) {
target.addEventListener("click", function() {
let subMenu = target.querySelector("ul");
subMenu.classList.add("show");
});
});
Try this by checking if ul has .show
let targets = document.querySelectorAll("li");
let main, sub;
targets.forEach(function(target) {
if (target.parentElement.parentElement.tagName == "DIV") { // for mains
target.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
let subMenu = target.querySelector("ul");
toggleClass(subMenu);
if (main && main != subMenu) {
main.classList.remove("show")
}
main = subMenu;
});
} else if (target.parentElement.parentElement.parentElement.parentElement.tagName == "DIV") {
target.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
let subMenu = target.querySelector("ul");
toggleClass(subMenu);
if (sub && sub != subMenu) {
sub.classList.remove("show")
}
sub = subMenu;
});
} else {
target.addEventListener("click", function(e) {
e.preventDefault();
e.stopPropagation();
});
}
});
function toggleClass(subMenu) {
if (subMenu.className.includes('show')) {
subMenu.classList.remove("show")
} else {
subMenu.classList.add("show");
}
}
ul {
display: none;
}
.show {
display: flex;
flex-direction: column;
}
<div class="container">
<div class="menu">
<ul class="show">
<li>
<span class="topLevel">Services</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
<li>
<span class="topLevel">Technology</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
<li>
<span class="topLevel">About</span>
<ul>
<li>
1
<ul>
<li>1.1</li>
<li>1.2</li>
<li>1.3</li>
</ul>
</li>
<li>
2
<ul>
<li>2.1</li>
<li>2.2</li>
<li>2.3</li>
</ul>
</li>
<li>
3
<ul>
<li>3.1</li>
<li>3.2</li>
<li>3.3</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
Try this code in order to toggle your list:
let
let targets = document.querySelectorAll("li");
targets.forEach(function(target) {
target.addEventListener("click", function() {
let subMenu = target.querySelector("ul");
let isOpen = subMenu.classList.contains('show');
if (!isOpen) {
subMenu.classList.add("show");
} else {
subMenu.classList.remove("show");
}
});
});
working jsfiddle
https://jsfiddle.net/vfxtcbpd/2/

Codepen Returning Empty Array When Expecting Few Items

I'm trying to console log a variable that should contain an array. However, in Codepen, it returns an empty array. I'm expecting it to return contents of the list item.
Here's the Codepen along w/ some snippet code:
This is a React project, btw, which is why I'm using className instead of class.
HTML
<ul className="nav">
<li>
<a>Products</a>
<div className="subnav-block">
<ul>
<li>
<a>Product A</a>
</li>
<li>
<a>Product B</a>
</li>
</ul>
</div>
</li>
<li>
<a>About</a>
<div className="subnav-block">
<ul>
<li>
<a>About Us</a>
</li>
<li>
<a>Press</a>
</li>
</ul>
</div>
</li>
</ul>
JS
const nav = document.querySelectorAll('.nav > li');
console.log(nav)
Codepen is returning:
[Object NodeList] {Length: 0}
Because className isn't how you add a HTML class - it's simply class.
const nav = document.querySelectorAll(".nav > li");
console.log(nav);
<ul class="nav">
<li>
<a>Products</a>
<div class="subnav-block">
<ul>
<li>
<a>Product A</a>
</li>
<li>
<a>Product B</a>
</li>
</ul>
</div>
</li>
<li>
<a>About</a>
<div class="subnav-block">
<ul>
<li>
<a>About Us</a>
</li>
<li>
<a>Press</a>
</li>
</ul>
</div>
</li>
</ul>
To keep using className, use an attribute selector:
const nav = document.querySelectorAll("[className='nav'] > li");
console.log(nav);
<ul className="nav">
<li>
<a>Products</a>
<div className="subnav-block">
<ul>
<li>
<a>Product A</a>
</li>
<li>
<a>Product B</a>
</li>
</ul>
</div>
</li>
<li>
<a>About</a>
<div className="subnav-block">
<ul>
<li>
<a>About Us</a>
</li>
<li>
<a>Press</a>
</li>
</ul>
</div>
</li>
</ul>
If this is in a react app make sure you are calling the code in the correct place so that the dom has actually been updated with your markup before the query is being called. If you are using a class component move your code inside componentDidMount
componentDidMount() {
const nav = document.querySelectorAll(".nav > li");
console.log(nav);
}
If you are using hooks place it inside useEffect
useEffect(() => {
const nav = document.querySelectorAll('.nav > li')
console.log(nav)
}, [])
here is a working example using hooks: https://codesandbox.io/s/currying-monad-t6104?fontsize=14

jQuery switching lists with same class

I created a bit complex list and I wish after clicking on element with class ".recipes", display it inside paragraph but after clicking on button prev or next, I wish to switch to previous or next element with same class name. I know already how to switch the list if they are next to each other but after putting them inside different parents it seems more complicated:
<div>
<button class="prev">prev</button>
<button class="next">next</button>
<p class="showRecipies"></p>
<ul class="listOfRecipies">
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Drinks</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Vegetables</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Meat</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Fruits</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Others</li>
</ul>
</li>
</ul>
</div>
To show clicked element:
$(".recipies").on("click", function() {
$(".recipies.active").add($(this)).toggleClass("active");
var recipieValue = $(this).text();
var showRecipies = document.querySelector(".showRecipies");
showRecipies.innerHTML = recipieValue;
});
and to show previous element with class "recipes" I got from another person on this forum is here however because classes have different parents now, it doesn't work, I thought that i can add more parents() and because of that find previous or next ".recipes" but it didn't work out:
$(".prev").click(function() {
var isFirst = $(".recipies.active").is(":first-child");
if(isFirst){
$(this).parent().find(".recipies:last").trigger("click");
} else {
$(".recipies.active").prev().trigger("click");
}
});
Check this out:
$(".recipies").on("click", function() {
$(".recipies.active").add($(this)).toggleClass("active");
var recipieValue = $(this).text();
var showRecipies = document.querySelector(".showRecipies");
showRecipies.innerHTML = recipieValue;
});
$(".prev").click(function() {
var isFirst = $(".recipies.active").text() == $($(".recipies").get(0)).text();
if (isFirst) {
$(this).parent().find(".recipies:last").trigger("click");
} else {
$(".recipies.active").parent().parent().prev().find(".recipies").trigger("click");
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>
<button class="prev">prev</button>
<button class="next">next</button>
<p class="showRecipies"></p>
<ul class="listOfRecipies">
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Drinks</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Vegetables</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Meat</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Fruits</li>
</ul>
</li>
<li>
<ul class="particularRecipie">
<li class="timeOfPreparation">3:20</li>
<li class="recipies">Others</li>
</ul>
</li>
</ul>
</div>

jQuery Pajinate: How to ignore hidden list-items?

I've a HTML list and want to add a simple pagination with the jQuery Plugin Pajinate. That works fine, but I would like Pajinate to ignore the hidden list-items.
For testing, here is the codesnippet on jsfiddl.net (same as below). The last two list-items should not be displayed and the the num of pages should be 3 instead of 4.
And ideas how to fix it? Thanks in advance!
<script>
$(function () {
$('#paging_container3').pajinate({
items_per_page: 5,
item_container_id: '.alt_content',
nav_panel_id: '.alt_page_navigation',
nav_label_first: '<<',
nav_label_last: '>>',
nav_label_prev: '<',
nav_label_next: '>'
});
});
</script>
<div id="paging_container3" class="container">
<h2>Custom List Size</h2>
<div class="alt_page_navigation"></div>
<ul class="alt_content">
<li>
<p>One</p>
</li>
<li>
<p>Two</p>
</li>
<li>
<p>Three</p>
</li>
<li>
<p>Four</p>
</li>
<li>
<p>Five</p>
</li>
<li>
<p>Six</p>
</li>
<li>
<p>Seven</p>
</li>
<li>
<p>Eight</p>
</li>
<li>
<p>Nine</p>
</li>
<li>
<p>Ten</p>
</li>
<li>
<p>Eleven</p>
</li>
<li>
<p>Twelve</p>
</li>
<li>
<p>Thirteen</p>
</li>
<li>
<p>Fourteen</p>
</li>
<li style="display: none">
<p>Fifteen (ignore me)</p>
</li>
<li style="display: none">
<p>Sixteen (ignore me)</p>
</li>
</ul>
</div>

Categories

Resources