materialize collapsible: how to determine which section is open? - javascript

I have a materialize collapsible which works as expected. Something similar to:
<ul class="collapsible">
<li>
<div class="collapsible-header">Title1</div>
<div class="collapsible-body" />
</li>
<li>
<div class="collapsible-header">Title2</div>
<div class="collapsible-body" />
</li>
</ul>
In a later process, when pressing a button I need a javascript function to modify its behavior depending on which section is open.
How can I determine which section is open?
I guess one possibility would be to store in a hidden element the index of the section when it is selected but I don't know how to do it.

Materializecss add an active class to an open collapsible item by itself. So you can use it to understand which collapsible item is open.
You can use this jquery code :
$(document).on("click","ul.collapsible li", function(){
var elem = document.querySelectorAll("ul.collapsible li");
var index = "none"
for (var i = 0; i < elem.length; i++) {
if (elem[i].className == "active") {
index = i;
}
document.getElementById("show").innerHTML = index;
}
})
This code show index of opened collapsible item for you.
Here is complete example : jsFiddle

Related

Active menu item NOT based on URL but based on a variable

I want to have a Menu with active items on my website. It should be added a class to activate the item. Since the project has cryptic URLs, URL-based solutions are not possible. The text of the respective menu item is shown in the respective page title.
My idea was to compare the pagetitle id="navtopheader" with the text in the menu item. If it is equal, add the menuactive class to the respective menu item.
My HTML looks basically like this:
<div id="navtopheader" class="navtopheader">menu item 1</div>
...
<div class="nav_ebene_2">
<p>menu item 1</p>
</div>
<div class="nav_ebene_2">
<p>menu item 2</p>
</div>
...
I can do it actually in an inefficient way:
var headertext = document.getElementById("navtopheader").innerText
var menutext0 = document.getElementsByClassName("nav_ebene_2")[0].innerText
var navlist = document.getElementsByClassName("nav_ebene_2");
if (headertext == menutext0) {
navlist[0].classList.add("activemenu");
}
var menuitem1 = document.getElementsByClassName("nav_ebene_2")[1].innerText
if (headertext == menuitem1) {
navlist[1].classList.add("activemenu");
}
...
But since the number of menu items varies across the website, i would get an error message if i include too much/too few menu items.
Is there an appropriate solution? I tried the whole day but didn't solve the problem. Thank you!
Iterate over the collection of <p>s that are children of nav_ebene_2. When a match is found, add the class to its parent.
const headerText = document.querySelector('#navtopheader').textContent;
for (const p of document.querySelectorAll('.nav_ebene_2 p')) {
if (p.textContent === headerText) {
p.parentElement.classList.add('activemenu');
}
}

Closing one element when opening another within the same loop using Javascript

first time on here so i'll try my best to explain what I'm asking.
So I have 3 list items with the same class name. I've put them in a looping function so that when you click on one it will display a sub set of list items for that specific list item. I also have them inside an if statement that adds a new class name to the specific list item that was clicked. It allows opening and closing of the sub list items when you click the corresponding parent element.
My question is; how can I use this same principle of checking for the additional class name, when the user clicks any of the list items. In other words, I am trying to code it in a way that will allow me to close any of the open sub list items when the user clicks a new list item.
This is what I came up with but it doesn't know what button[i] is when I include it within the "click" function. What I was trying to do with this code is to take whatever list item was clicked, and then check the previous and next iterations of the class name "button" to see if any of the contain also contain the class name "clicked.
HTML
<div class="main">
<ul>
<li>One
<ul>
<li>One-1</li>
<li>One-2</li>
</ul>
</li>
<li>Two
<ul>
<li>Two-1</li>
<li>Two-2</li>
</ul>
</li>
<li>Three
<ul>
<li>Three-1</li>
<li>Three-2</li>
</ul>
</li>
</ul>
</div>
CSS
.main ul ul {
display: none;
}
.main ul ul li {
display: block;
}
Javascript
var button = document.getElementsByClassName("button");
for (i = 0; i < button.length; i++) {
button[i].addEventListener("click", function() {
var prevItem = button[i - 1];
var nextItem = button[i + 1];
if (prevItem.className !== "button") {
prevItem.className = "button";
prevItem.nextElementSibling.style.display = "none";
}
if (nextItem.className !== "button") {
nextItem.className = "button";
nextItem.nextElementSibling.style.display = "none";
}
if (this.className === "button") {
this.className += " clicked";
this.nextElementSibling.style.display = "block";
}
});
}
I am wanting to make this code usable no matter how many list items you add. So checking exactly button[0] button[1] and button[2] wasn't really an option, but I can see how button[i + 1] might not check every list item after it but rather just the next one. I tried adding another loop but ran into similar issues. anyway that's why I'm here. Thanks for any help in advance.
Since I am not sure whether I understood your question correctly, I quickly rephrase it in my own words.
Question: "I have an arbitrary number of list elements, of which each contains a button and a nested list. The button is always visible, the nested list is hidden by default. When the user clicks on a button, the corresponding nested list should be shown. At the same time, all other shown nested lists should be hidden again. How can I achieve this?"
The original HTML looks fine:
<div class="main">
<ul>
<li>One
<ul>
<li>One-1</li>
<li>One-2</li>
</ul>
</li>
<li>Two
<ul>
<li>Two-1</li>
<li>Two-2</li>
</ul>
</li>
<li>Three
<ul>
<li>Three-1</li>
<li>Three-2</li>
</ul>
</li>
</ul>
</div>
The CSS I did not fully understand, but I suggest the following:
.main ul ul {
display: none;
}
.main li.is-active ul {
display: block;
}
.main ul ul li {
display: block;
}
By adding the "is-active" class to an LI element, it is shown. This way, the CSS controls the visibility.
For the JavaScript part, I suggest this:
const buttonElements = Array.from(document.querySelectorAll('.button'));
buttonElements.forEach(buttonElement => {
buttonElement.addEventListener('click', () => {
const activeElements = Array.from(document.querySelectorAll('.is-active'));
activeElements.forEach(activeElement => {
activeElement.classList.remove('is-active');
});
buttonElement.parentElement.classList.add('is-active');
});
});
This solution assumes you can use newer versions of JavaScript/ECMAScript. Overall, it makes use of const and arrow functions.
First, we get all elements with the class "button" by using document.querySelectorAll(). Since the result is a NodeList and no array, we convert it using Array.from(). Afterwards, we loop through the array by using Array.prototpye.forEach(). We add an event listener for the "click" event. When a button is clicked, we search for all elements with the "is-active" class and for each one remove it. Finally, we add the "is-active" class to the parent element of the clicked button using Node.prototype.parentElement().
Here is another solution that works in older browsers:
var buttonElements = document.getElementsByClassName('button');
for (var i = 0; i < buttonElements.length; i++) {
buttonElements[i].addEventListener('click', function(event) {
var activeListElements = document.getElementsByClassName('is-active');
for (var i = 0; i < activeListElements.length; i++) {
activeListElements[i].setAttribute('class', '');
}
event.target.parentNode.setAttribute('class', 'is-active');
});
}
This is pretty much the same as the other approach but works with older versions of JavaScript.
Generally, the idea is to focus on an arbitrary sum of elements instead of an array with a specific length. In natural language something like: "Give me all buttons. For every button, add an event listener. When a button is clicked, give me all active list elements and remove their active status. Then, mark the list item above the button as active".
Hope this helps

Click on li with specific "rel" : Pure javascript, no jquery

I have to do a fully JavaScript (jQuery forbidden) script. I only can use the 'click()' function.
I have to simulate a click on a "li" which has a random attribute for a sneakers website.
For showing you :
The user click on a "mega menu" which display (on the click) the different sizes.
The user choose his size in that menu
I show you the "code" of the html big menu. On this article, "LZAE31" is the ID product.
<ul class="theUlClass" style="display:none">
<li class="theLiClass" rel="LZAE31:40">EU 40</li>
<li class="theLiClass" rel="LZAE31:41">EU 41</li>
<li class="theLiClass" rel="LZAE31:42">EU 42</li>
<li class="theLiClass" rel="LZAE31:43">EU 43</li>
</ul>
Imagine that the user has already buy something on the website, and his size is 42 EU.
The script is 90% ok (the part that the user has already buy something), but now i need to "simulate" a click.
For exemple, the end of the script works and it is :
document.getElementsByClassName("addtocart")[0].click();
So I really need you, to understand how can i click on the "rel="RANDOM:42" in the li.TheLiClass for exemple...
Try with:
First select element:
With Selector:
document.querySelector("[rel='LZAE31:40']")
// try like: console.log(document.querySelector("[rel='LZAE31:40']"));
Or with this function :
function specificRel( rel )
{
var items = document.getElementsByTagName('li');
for (var i=0; i<items.length; i++) {
if (items[i].getAttribute("rel") == rel) {
return items[i];
}
}
}
// try like: console.log(specificRel("LZAE31:40"));
And two, click :
With selector:
document.querySelector("[rel='LZAE31:40']").click();
Or with the function:
specificRel("LZAE31:40").click();
It's a tad bit unclear what you're asking, but I'll give it a shot. To get the LI element with rel=LZAE31:43 is trivial.
for( var a= document.getElementsByTagName('li'),i= 0; i < a.length; ++i )
if( a[i].getAttribute('rel') == 'LZAE31:43' )
break;

Jquery to copy first link to second ul and then change class of first link

Short question:
http://jsfiddle.net/wF4FH/2/
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
Long question:
I'm having problem finding the correct way to insert an li element based on the first link. The problem is I cant use id's on my markup so I have to "walk through" each class and check for names. I might just make this a lot more complicated than it is because my first two solutions didn't work the way I thought they would.
html
<ul class="nav">
<li class="active">
Start
</li>
<li class="has-child">
page1
<ul class="">
<li>
page2
</li>
</ul>
</li>
<li class="has-child">
page10
<ul class="">
<li>
page20
</li>
<li>
page30
</li>
</ul>
</li>
javascript
//Copy first link to child ul li
var pageLinks = $("li.has-child > a:first-child");
if (pageLinks != null) {
//var dropdownMenus = $("li.dropdown > a:first-child");
for (var i = 0; i < pageLinks.length; i++) {
for (var x = 0; x < pageLinks.length; x++) {
if (pageLinks[i].innerHTML === pageLinks[x].innerHTML) {
pageLinks[x].childNodes.append(pageLinks[i]);
}
}
}
}
//Change css classes
$("li.has-child").attr('class', 'dropdown');
$(".dropdown ul").addClass("dropdown-menu");
$(".dropdown a").attr("href", "#").addClass("dropdown-toggle").attr('data-toggle', 'dropdown');
strong text
What I want is for Page1 to be right above Page2 and Page10 above Page 20 before I change the classes. This should work for any number of elements.
When they are copied to the inner ul I change the top level menu item to a different class to work as a clickable dropdown men item.
The code provided gives an "Uncaught TypeError: Object # has no method 'append' ".
It is the navigation of a cms I cant change the markup on.
try this:
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link)))
})
}
http://jsfiddle.net/wF4FH/6/
You need .clone() method to copy elements..
UPDATED
$links = $('li.has-child').children('a:first-child');
if($links.length > 0){
$links.each(function(i,link){
$(link).next().prepend($('<li></li>').append($(link).clone()))
})
}
http://jsfiddle.net/wF4FH/7/
When you have a jQuery object and you access it by numeric index, you're left with an HTML element. So $('body')[0] == document.body. This means that when you access pageLinks[x], you're really getting a raw element. This means that you want pageLinks[x].appendChild(pageLinks[i]);, not pageLinks[x].childNodes.append(pageLinks[i]);

Retaining state of navigation problems

Hi I am trying to retain the state of a navigation accros multiple pages pages.I have managed to get the indexes of each element I want to retain , the problem is setting them.For some reason only some of the elements seem to be set.
I have debugged the code and it seems as if only one element gets taken into consideration sometimes.I can not figure out what am I doing wrong here.Here is my code:
<ul id="ProductNav">
<li>
<h2>#category.Key.ToUpper()</h2>
<ul>
<li>
<img src="#Url.Content("~/Content/Images/arrow.gif")" class="arrow"/>
#Html.ActionLink(subcategory,"Index" , "Products" , new { subcat = subcategory} , null)
</li>
</ul>
</li>
</ul>
......
var menuState = JSON.parse(sessionStorage["navigation"]);
for (var i = 0; i < menuState.length; i++) {
var menuIndex = menuState[i].eq;
$("ul#ProductNav li").eq(menuIndex).children("ul").css("display", "block");
}
menuState will we an object containing an array of indexes
From what I understand so far the problem begins here:
$("ul#ProductNav li").eq(menuIndex).children("ul").css("display", "block");

Categories

Resources