Hi and I hope someone can help.
I'm building a website which will have a main horizontal tabbed menu and a secondary horizontal tabbed menu when one of the items above is clicked. Off the second tabbed menu there will be another sub menu which bring up links where, once clicked, will reveal content to the right. Pictorially this is shown below.
To try and get the logic working I've built some very simple test pages to try out showing and hiding divs but I only seem to be able to get the equivalent of the first sub menu showing.
Because (ultimately) this will be a large site with multiple pages I've organised the first menu in the root folder of the site, the sub menu in a sub folder called 'pages' and the sub sub menu in a folder called 'pages/resource_pages'.
Here's my test code, 1 the top level - nest.html in the root folder
<body onload="openLevel2();"> <!-- done to initially hide unwanted divs -->
<div id="home">Nested Div Test</div>
</br>
<div id="div1"> Nest 1 - located in root folder </div>
<div id="nest1" class="level1HiddenDiv">
<script>$( "#nest1" ).load( "pages/nest2.html" );</script>
</div>
</body>
Second level code - nest2.html in the pages folder
<body>
<div id="div2"> Nest 2 - located in root/pages folder </div>
<div id="nest2" class="level2HiddenDiv">
<script>
$( "#nest2" ).load( "pages/resource_pages/nest3.html" );
</script>
</div>
</body>
Third level code - nest3.html in the pages/resource_pages sub folder
<body>
<div id="div3"> Nest 3 - located in root/pages/resource_pages folder </div>
<div id="nest3" class="level3HiddenDiv">
<script>
$( "#nest3" ).load( "ca_nearby.html" );
</script>
</div>
</body>
Here' my javascript
function openLevel2(evt, scriptName) {
// Declare all variables
var i, level1Hyperlink, level1HiddenDiv;
// Get all elements with class="level1HiddenDiv" and hide them
level1HiddenDiv = document.getElementsByClassName("level1HiddenDiv");
for (i = 0; i < level1HiddenDiv.length; i++) {
level1HiddenDiv[i].style.display = "none";
}
// Get all elements with class="level1Hyperlink" and remove the class "active"
level1Hyperlink = document.getElementsByClassName("level1Hyperlink");
for (i = 0; i < level1Hyperlink.length; i++) {
level1Hyperlink[i].className = level1Hyperlink[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(scriptName).style.display = "block";
evt.currentTarget.className += " active";
}
// Script for showing resource letter menu tabs
function openLevel3(evt, resourceName) {
// Declare all variables
var i, level2Hyperlink, level2HiddenDiv;
// Get all elements with class="level2Hyperlink" and hide them
level2HiddenDiv = document.getElementsByClassName("level2HiddenDiv");
for (i = 0; i < level2HiddenDiv.length; i++) {
level2HiddenDiv[i].style.display = "none";
}
// Get all elements with class="level2Hyperlink" and remove the class "active"
level2Hyperlink = document.getElementsByClassName("level2Hyperlink");
for (i = 0; i < level2Hyperlink.length; i++) {
level2Hyperlink[i].className = level2Hyperlink[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(resourceName).style.display = "block";
evt.currentTarget.className += " active";
}
// Script for showing resource letter sub menu tabs
function openLevel4(evt, letterName) {
// Declare all variables
var i, level3Hyperlink, level3HiddenDiv;
// Get all elements with class="level3HiddenDiv" and hide them
level3HiddenDiv = document.getElementsByClassName("level3HiddenDiv");
for (i = 0; i < level3HiddenDiv.length; i++) {
level3HiddenDiv[i].style.display = "none";
}
// Get all elements with class="level3Hyperlink" and remove the class "active"
level3Hyperlink = document.getElementsByClassName("level3Hyperlink");
for (i = 0; i < level3Hyperlink.length; i++) {
level3Hyperlink[i].className = level3Hyperlink[i].className.replace(" active", "");
}
// Show the current tab, and add an "active" class to the link that opened the tab
document.getElementById(letterName).style.display = "block";
evt.currentTarget.className += " active";
}
Can anyone spot what I'm doing wrong?
Thanks for your interest.
Messing around with a lot js and html, you can run into problems concerning readability, so i would prefer a pure js solution, wich will make it more readable /better debuggable:
var structure={
name:"level1",
links:[
{
name:"level2-1",
links:[
{
name:"level3-1",
links:[]
},
{
name:"level3-2",
links:[]
}
]
},
{ name:"level2-2", ...
}
]
};
function show(element){
//add the name to header
document.GetElementById("header").innerHTML=element.name;
linkcontainer=document.getElementById("linkcontainer");
linkcontainer.innerHTML="";
counter=0;
element.links.forEach(function(link){
l=document.createElement("span");
l.onclick=(function(element,counter){
//this should create an onclick element
return function(){show(element.links[counter])};
})(element,counter);
l.innerHTML=element.links[counter].name;
linkcontainer.appendChild(l);
counter++;
}
}
window.onload=function(){show(structure);}
</script>
<div id="header">should contain name</header>
<div id="linkcontainer">should contain links</div>
This should create:
Level1
Level2.1
Level2.2
And if you click on level 2.1:
Level2.1
Level3.3
Level3.4
I had problems with the onclick statement: http://www.howtocreate.co.uk/referencedvariables.html
Related
I am trying to check if an element in an array has a specific class, and if it doesn't, add that class. This is happening when a segment of a pie chart created with chart.js is clicked. When a segment is clicked, a corresponding hidden div should appear, and any unhidden divs should disappear. When the segment is clicked, a class is removed from the relevant div to unhide it, and any unhidden divs should have the class added back to hide it again.
My issue is that I am not sure how to loop through the array to check if any divs don't have the class, and if they don't, to add the class back.
HTML
<div id="mod0" class="m-d-hide">Some content</div>
<div id="mod1" class="m-d-hide">Some content</div>
<div id="mod2" class="m-d-hide">Some content</div>
<div id="mod3" class="m-d-hide">Some content</div>
Javascript
var activePoints = window.modulePie1.getElementsAtEvent(event);
var modDescription = [];
for(var i=0; i<9; i++) {
modDescription[i] = document.getElementById("mod"+i);
if (activePoints.length > 0) {
var clickedSegmentIndex = activePoints[0]._index;
if (clickedSegmentIndex==[i]) {
modDescription[i].classList.remove("m-d-hide");
//Everything works until here - can't add class
if (!modDescription.classList.contains("m-d-hide")) {
modDescription.classList.add("m-d-hide");
}
}
}
}
Thanks in advance for any help.
I've managed to fix this issue by adding an else statement to the
if (clickedSegment==[i])
loop, so there was no need to loop through the array and check all the classes.
Code now looks like:
var activePoints = window.modulePie1.getElementsAtEvent(event);
var modDescription = [];
for(var i=0; i<9; i++) {
modDescription[i] = document.getElementById("mod"+i);
if (activePoints.length > 0) {
var clickedSegmentIndex = activePoints[0]._index;
if (clickedSegmentIndex==[i]) {
modDescription[i].classList.remove("m-d-hide");
} else {
if (!modDescription[i].classList.contains("m-d-hide")) {
modDescription[i].classList.add("m-d-hide");
}
}
}
}
I had this code, that works ok to show/hide a second element by clicking the first one:
<script>
var acc = document.getElementsByClassName("movs-header");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
var x = this.nextElementSibling;
if (x.style.display === "block") {
x.style.display = "none";
} else {
x.style.display = "block";
}
}
}
</script>
And this is the structure (they are repeating elements in a php system):
<div id="frm_container_[id]" class="movs-box">
<div class="movs-header">
some content here, clickable to show-hide the next sibling div
</div>
<div class="movs-body">
this content will show and hide
</div>
</div>
Now, I need to add this link inside a div with a class="movs-editlink", which has to be outside the movs-box div to refer the id "frm_container", in order to work.
Then the structure will be:
<div id="frm_container_[id]" class="movs-box">
<div class="movs-header">
some content here, clickable to show-hide the next sibling div
</div>
<div class="movs-body">
this content will show and hide
</div>
</div>
<div class="movs-editlink">[editlink label="edit" prefix="frm_container_"]</div> <!-- this div to show and hide along -->
(please don't mind the shortcode, it works fine)
What I need is to show/hide the last div with the same javascript code (when I click the "movs-header" div, but I fail to refer to "this.className", my guess was:
<script>
var acc = document.getElementsByClassName("movs-header");
var i;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function(){
this.classList.toggle("active");
var x = this.nextElementSibling;
var xedit = this.getElementsByClassName("movs-editlink").classname;
if (x.style.display === "block") {
x.style.display = "none";
xedit.style.display = "none";
} else {
x.style.display = "block";
xedit.style.display = "block";
}
}
}
</script>
I believe this is not working because the last div is outside the scope of "this", then I think I need to find the NEXT div in the structure with the class "movs-link" to be included in the display toggle, am I right? But I can't find how. Please help.
Based on your markup, rather than getElementsByClassName from this, do it with this.parentNode.nextElementSibling
var xedit = this.parentNode.nextElementSibling;
Or with jquery's nextUntil
var xedit = $(this).parent().nextUntil( "movs-editlink" );
I have a drop down menu list of links fixed on the left side of a web page I'm in the process of building. As of now, everything is working fine except I would like to have one more functionality; maintaining the position of my drop down menu after clicking links. The links will have the exact same drop down menu except that the title and content of the new page will change. Here are the core pieces I currently have in terms of code. If you click one of the headers it will drop down with the links available. Note, only one drop down is allowed to be open. If I had .html files for the links and clicked one, the page will "refresh" and the drop down menu will be shown as closed again losing its position before the link was clicked. I don't know how to modify the code to do what I want so I was hoping someone can help me. I would like to stick with just HTML, JS, and CSS if that's possible. Thanks, Here's the JS straight from the link
// Gotta give credit where credit is due
// https://stackoverflow.com/questions/45911424/have-only-one-drop-down-panel-open-on-click-using-html-js-and-css
var acc = document.getElementsByClassName("drop-down");
var i = 0;
for (i = 0; i < acc.length; i++) {
acc[i].onclick = function () {
var panel = this.nextElementSibling;
var maxHeight = panel.style.maxHeight;
//Collapse all divs first
var divs = document.getElementsByClassName("drop-down-panel");
for (i = 0; i < divs.length; i++) {
divs[i].style.maxHeight = null;
divs[i].previousElementSibling.classList.remove("active");
}
this.classList.toggle("active")
if (maxHeight) {
panel.style.maxHeight = null;
this.classList.remove("active");
} else {
panel.style.maxHeight = panel.scrollHeight + "px";
}
};
}
I have this simple dropdown faq system, I only want one content div to be open at a time, so I tried to use an if / else condition, but I can only make it work halfway.
I'm checking if the content div next to the trigger div has class is-visible — if not, add that class (this works)
But if the previous content div has (contains) class is-visible, I want to remove it, so only one content div is open at a time.
I've tried so many different conditions but I think I'm overcomplexifying it, this should be simple enough right?
https://jsfiddle.net/notuhm05/1/
var faqTrigger = document.querySelectorAll('.mm-faq-trigger');
for (var i = 0; i < faqTrigger.length; i++) {
faqTrigger[i].addEventListener('click', function() {
if (!this.nextElementSibling.classList.contains('is-visible')) {
this.nextElementSibling.classList.add('is-visible');
} else if (this.previousElementSibling.classList.contains('is-visible')) {
this.nextElementSibling.classList.remove('is-visible');
} else {
console.log("doesn't work");
}
});
}
Would greatly appreciate some pointers here! :-)
Here is a working solution:
Toggle the class is-visible on the clicked node
Iterate through all triggers and remove the class is-visible if the id of the href tag does not match the clicked nodes id. NOTE: I had to add an id property to the trigger href tag like <a id="1" href="#" class="mm-faq-trigger">Trigger</a>
Source Code:
var faqTrigger = document.querySelectorAll('.mm-faq-trigger');
for (var i = 0; i < faqTrigger.length; i++) {
faqTrigger[i].addEventListener('click', function() {
this.nextElementSibling.classList.toggle('is-visible');
for (var i = 0; i < faqTrigger.length; i++) {
var trigger = faqTrigger[i];
if (trigger.nextElementSibling !== null && trigger.id !== this.id) {
trigger.nextElementSibling.classList.remove('is-visible');
}
}
});
}
I have a search page, and I found a script for a tab component which seperates the TV-Show search page and Movie search page. When a user clicks on lets say the "Movie" tab, and perform a search from there, I make the form add ?type=movie&... to the url. But, when they perform the search, the tab that's selected is the the "TV-Show" tab, and the have to click over to get to the other tab which has the results they want.
Here is the code, and in it is some of what I tried:
the HTML looks like this:
<article class="first">
<h2>Search</h2>
<hr/>
<div id="tabWrapper">
<div id="tabContainer">
<div class="tabs">
<ul>
<li id="tabHeader_1">TV Shows</li>
<li id="tabHeader_2">Movies</li>
</ul>
</div>
<div class="tabContent">
<div class="tabpage" id="tabpage_1">
<?php
if (isset($sortFilt['year']))
unset($sortFilt['year']);
if ($sortField=="year")
$sortField==" ";
DisplaySearchPage("shows", $terms, $sortField, $sortDir, $sortFilt, $page, $perPage);
?>
</div>
<div class="tabpage" id="tabpage_2">
<?php
DisplaySearchPage("movies", $terms, $sortField, $sortDir, $sortFilt, $page, $perPage);
?>
</div>
</div>
</div>
</div>
</article>
<script src="tabs.js"></script>
And this is the the tabs.js script:
window.onload=function() {
// get tab container
var container = document.getElementById("tabContainer");
// set current tab
var navitem = container.querySelector(".tabs ul li");
//store which tab we are on
var ident = navitem.id.split("_")[1];
navitem.parentNode.setAttribute("data-current",ident);
//set current tab with class of activetabheader
navitem.setAttribute("class","tabActiveHeader");
//hide two tab contents we don't need
var pages = container.querySelectorAll(".tabpage");
for (var i = 1; i < pages.length; i++) {
pages[i].style.display="none";
}
//this adds click event to tabs
var tabs = container.querySelectorAll(".tabs ul li");
for (var i = 0; i < tabs.length; i++) {
tabs[i].onclick=displayPage;
}
// This below is is what I tried.
var selTab = (getUrlVars()["type"] == "movies") ? 2 : 1;
var selTab = (getUrlVars()["type"] == "movies") ? 1 : 0; // Tried this too.
tabs[selTab].click();
// its seems like it should have worked, what am I doing wrong.
}
// on click of one of tabs
function displayPage() {
var current = this.parentNode.getAttribute("data-current");
//remove class of activetabheader and hide old contents
document.getElementById("tabHeader_" + current).removeAttribute("class");
document.getElementById("tabpage_" + current).style.display="none";
var ident = this.id.split("_")[1];
//add class of activetabheader to new active tab and show contents
this.setAttribute("class","tabActiveHeader");
document.getElementById("tabpage_" + ident).style.display="block";
this.parentNode.setAttribute("data-current",ident);
}
How can I adapt this script to let me decide which tab is displayed when the page is loaded based on what the type variable is in the URL parameters?
I found the answer. My problem was with getUrlVars["type"], it failed to give me the value of the 'type' url variable, so I created my own function for it. Here is the working code if anyone else is wondering how to get variables from the url.
function getURLParameter(name)
{
return decodeURI(
(RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
);
}
window.onload = function()
{
// Get tab container
var container = document.getElementById("tabContainer");
// Set the current tab.
var navitem = container.querySelector(".tabs ul li");
// Store which tab is selected.
var ident = navitem.id.split("_")[1];
navitem.parentNode.setAttribute("data-current", ident);
// Set the current tab with a class of 'activetabheader'.
navitem.setAttribute("class", "tabActiveHeader");
// Hide the tab contents we don't need.
var pages = container.querySelectorAll(".tabpage");
for (var i = 1; i < pages.length; i++) {
pages[i].style.display = "none";
}
// This adds the click event handler to the tabs.
var tabs = container.querySelectorAll(".tabs ul li");
for (var i = 0; i < tabs.length; i++) {
tabs[i].onclick = displayPage;
}
// Selects tab based on 'type' url variable.
if (getURLParameter("type") == "movies") {
tabs[1].click();
}
}
// OnClick() event handler for tabs.
function displayPage()
{
var current = this.parentNode.getAttribute("data-current");
// Remove class of 'activetabheader' and hide the old contents.
document.getElementById("tabHeader_" + current).removeAttribute("class");
document.getElementById("tabpage_" + current).style.display = "none";
var ident = this.id.split("_")[1];
// Add a class of 'activetabheader' to new active tab and show contents.
this.setAttribute("class", "tabActiveHeader");
document.getElementById("tabpage_" + ident).style.display = "block";
this.parentNode.setAttribute("data-current", ident);
}