Making dropdown visible javascript - javascript

I made a dropdown system with razor script in Umbraco. I finally got all the children working but how do I hide the sub items that should be a dropdown list? I tried something with javascript but that seemed to open all dropdowns instead of each one individually.
Here's my code:
<div class="col-sm-3">
<div class="well well-lg span-padding extra-padding top background-light">
<ul class="nav nav-list tree">
# {
var documentRootNodeId = Model.Content.GetPropertyValue("documentRoot", true); // Fetch recursive document root id.
var selection = Umbraco.TypedContent(documentRootNodeId).Children.Where("Visible"); // Select all nodes below the document root.
}
#foreach(var item in Model.Content.Ancestor(1).Descendants("menu")) {
foreach(var ItemChild in #item.Children("hoofdstuk")) {
<li> #ItemChild.Name </li>
foreach(var Subitem in #ItemChild.Children) {
<li> #Subitem.Name </li>
if (Subitem.Children.Any()) {
foreach(var Finalitem in #Subitem.Children) {
<ul>
<li> < a href = "#Finalitem.Url" > #Finalitem.Name </a></li>
</ul>
}
}
}
}
}
}
</ul>
</div>
</div>
Javascript:
function myFunction(a) {
var itemcount = a.parentNode.getElementsByClassName("sub").length;
for (i = 0; i < itemcount; i++) {
a.parentNode.getElementsByClassName("sub")[i].classList.toggle("show");
}
}
This is what it displays:
Sub-Subtest1 and 2 needs to be hidden till we click it's ancestor which is SubTest1. Same goes for sub-subtest9.
I can't seem to get it working dynamically. If I create something with javascript it opens all the dropdown's at once and not individually.

First, I would recommend you to use Visual Studio, that will help you to fix issues with your code. You should fix your html structure and nest the elements of your list properly. You have li as direct children of li which is invalid.
<div class="col-sm-3">
<div class="well well-lg span-padding extra-padding top background-light">
<ul class="nav nav-list tree">
#{
var documentRootNodeId = Model.Content.GetPropertyValue("documentRoot", true); // Fetch recursive document root id.
var selection = Umbraco.TypedContent(documentRootNodeId).Children.Where("Visible"); // Select all nodes below the document root.
}
#foreach (var item in Model.Content.Ancestor(1).Descendants("menu"))
{
foreach (var ItemChild in item.Children("hoofdstuk"))
{
<li class="item">
#if (ItemChild.Children.Any())
{
<ul class="submenu">
#foreach (var Subitem in ItemChild.Children)
{
<li class="item">
#Subitem.Name
#if (Subitem.Children.Any())
{
foreach (var Finalitem in Subitem.Children)
{
<ul class="submenu">
<li> #Finalitem.Name </li>
</ul>
}
}
</li>
}
</ul>
}
</li>
}
}
</ul>
</div>
</div>
Then you can use jQuery to easily show the submenus. Use [children][1] to retrieve only the first level of submenus:
$(".item").click(function(){
$(this).children(".submenu").toggle(); //it will display or hide your submenu when clicking the item.
});
And your submenu should be hidden by default, you can do that with css:
.submenu{
display: none;
}
Note: to make the code more clear I would recommend you to use Razor helpers to create a recursive function for your sublists.

Related

Only show certain <div> if there's a certain element on the page

I'm trying to get the company I'm at a Help Centre set up, using Zendesk.
I've managed to implement a sidenav, but I'm struggling to make it show different anchor links depending on the category of the Help Centre the user is on. Zendesk only allows you to edit the HTML of the category page template, and I'm unable to dynamically load in the links.
Can anyone please advise on how to show DIV_1, only if the page contains <li title="Using ProductName">? I've searched but can't seem to find anything relevant.
From there I'll do the same for the other sections in the same way (e.g. only show DIV_2 if the page contains <li title="Developer Portal".
For reference, I have access to the category's HTML template, the CSS and JS.
Thanks in advance!
<div class="container">
<nav class="sub-nav">
<ol class="breadcrumbs">
<li title="Help Centre">
Help Centre
</li>
<li title="Using ProductName">
Using ProductName
</li>
</ol>
<div id="DIV_1">
<ul id="UL_2">
<li id="LI_1">
Admin and Settings
</li>
<li id="LI_1">
Getting Started
</li>
<li id="LI_1">
Content Types and Sources
</li>
<li id="LI_1">
Content Management
</li>
<li id="LI_1">
Content Publishing
</li>
<li id="LI_1">
Apps
</li>
<li id="LI_1">
Analytics
</li>
<li id="LI_1">
Troubleshooting
</li>
</ul>
</div>
</div>
</div>
You can use the built-in DOM query methods to accomplish this. In this case, you'd want to combine an if condition with the query, something like so:
if (document.querySelector('li[title="Using ProductName"]')) {
// make #DIV_1 visible however you please here
document.querySelector('#DIV_1').display = 'block';
}
If the li with the title Using ProductName does not exist, #DIV_1 will stay invisible; if it does, it will be shown.
You can do a quick for loop check:
var items = document.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
if (items[i].title == titleToCheckFor) { showElement(); }
}
You can fill in titleToCheckFor with the title you're looking for ("Using _____") and the showElement function would display the div, or you could just show the div right in the loop.
Using DOM query method querySelector you can search the target element, by default we set all div's hidden, and then we show only the required.
<style>
.module {
display:none;
}
</style>
<script>
// by default we show MODULE A else show module B
var module = "DIV_1";
if (document.querySelector('li[title="Developer Portal"]')) {
module = "DIV_2";
}
// we show the respective DIV
document.querySelector('.' + module).display = 'block';
</script>
<div class="module DIV_1" id="DIV_1">
...
</div>
<div class="module DIV_2" id="DIV_2">
....
</div>
You can achieve this via CSS classes.
SOLUTION 1:
This being the sample HTML:
<div id="Div_1" class="menu-div using-productname">
</div>
<div id="Div_2" class="menu-div help-centre">
</div>
<div id="Div_3" class="menu-div other-tab">
</div>
Now you should setup your css like:
.menu-div {
display: none;
}
So all menu divs are hidden by default when the page loads
Now when you move to some tab suppose "Using ProductName", all you need to do is
var title = "Using ProductName"; //Get the title
var className = title.split(" ").join("-").toLowerCase(); //Convert it to the correct class which matches with your Divs in the menu
document.querySelector(".menu-div").style.display = "none"; //Set all menu divs to hidden
document.querySelector("." + className).style.display = "block"; //Show the desired menu div
SOLUTION 2:
This being the sample HTML:
<div class="parent-div">
<div id="Div_1" class="menu-div">
</div>
<div id="Div_2" class="menu-div">
</div>
<div id="Div_3" class="menu-div">
</div>
Now you should setup your css like:
.parent-div .menu-div {
display: none;
}
.parent-div.using-productname #Div_1 {
display: block;
}
.parent-div.help-centre #Div_2 {
display: block;
}
.parent-div.other-tab #Div_3 {
display: block;
}
Now when you move to some tab suppose "Using ProductName", all you need to do is
var title = "Using ProductName"; //Get the title
var className = title.split(" ").join("-").toLowerCase(); //Convert it to the correct class which you will add to the parent
document.querySelector(".parent-div").className = "parent-div " + className; //Set the parent div class to the className - the css will take care of the rest!
NOTE - Also you should use different ids on your different LIs and A tags.
You can use jQuery in Zendesk Help Centers so
var test = $('.breadcrumbs').children(':contains(amy)')
if(test.length > 0) {
do something here like
$('#LI_1').hide();
}
It's kind of simple brute force, but it works.

Target element by attr then append class jQuery

I have a li tag that looks like :
<li class="item maybe" data-selected="1"></li>
<li class="item confused" data-selected="2"></li>
<li class="item why" data-selected="3"></li>
I then have three other elements that look like :
<li class="display"></li>
<li class="display"></li>
<li class="display"></li>
I'm trying to on select of one of the elements append the class to my 2nd list of elements.
So if I selected the li tag with data-selector="1" then my first li tag in my display list will have the class maybe added.
This is what i've tried so far, but i'm getting undefined when I console log my var out :
if ($('.item').attr('data-selected') == 1) {
var itemClassAdd = $('.item').find("[data-selected='1']").attr('class');
console.log(itemClassAdd);
$(".display").addClass(itemClassAdd);
}
Thanks!
I perform action in a click event for convenience:
$("li.item").click(function(){
var index = $(this).data("selected");
$("li.display").eq(index - 1).addClass($(this).attr("class"));
});
If you don't need class item:
$("li.item").click(function(){
var index = $(this).data("selected");
var c = $(this).attr("class").replace("item ", "");
$("li.display").eq(index - 1).addClass(c);
});
The class and the attribute are on the same element, so you use a compound selector, not find:
var itemClassAdd = $('.item[data-selected="1"]').attr('class');
But note that
if ($('.item').attr('data-selected') == 1) {
will only branch of the first element matching .item has data-selected="1", which may or may not be what you want.
Live Example:
if ($('.item').attr('data-selected') == 1) {
var itemClassAdd = $('.item[data-selected="1"]').attr('class');
$(".display").addClass(itemClassAdd);
}
.maybe {
color: green;
}
<ul>
<li class="item maybe" data-selected="1">maybe</li>
<li class="item confused" data-selected="2">confused</li>
<li class="item why" data-selected="3">why</li>
</ul>
<ul>
<li class="display">d1</li>
<li class="display">d2</li>
<li class="display">d3</li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

Using jQuery .nextUntil() on split lists

Is it possible to get the .nextUntil() to work on split lists, or get the same functionality?
So I am trying to implement the ever so popular shift select for my items, and since they are ordered in a list in my application I want to be able to select across <ul> borders.
I have the following set of DOM elements:
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
And using something like this:
$('li.clicked').nextUntil('li.selected');
I'd like a list containing the following elements
[ <li class="item">third</li>,
<li class="item">fourth</li>,
<li class="item">fifth</li> ]
However all I get is the elements leading up to the split </ul>. Is there any way of doing this? I have also tried to first selecting all items with $('.item')and then using .nextUntil() on them without any luck.
Is this what you are looking for?
$('li').slice($('li').index($('.clicked'))+1,$('li').index($('.selected')));
For reference
Jquery.Index
Jquery.Slice
Edit
So if you do
$('li')
you will get an array of all elements 'li' getting:
[<li class=​"item">​first​</li>​,
<li class=​"item clicked">​second​</li>​,
<li class=​"item">​third​</li>​,
<li class=​"item">​fourth​</li>​,
<li class=​"item">​fifth​</li>​,
<li class=​"item selected">​sixth​</li>​,
<li class=​"item">​seventh​</li>​]
Since it is an array you can slice him to get an sub array you just need two positions, where to start and here to finish.
//start
$('li').index($('.clicked'))+1 // +1 because you dont want to select him self
//end
$('li').index($('.selected'))
For better preformance you should before create an array with all li so it will not search all dom 3 times for the array of 'li'
var array = $('li');
var subarray = array.slice(array.index($('.clicked'))+1,array.index($('.selected')));
Assuming these lists cannot be merged into one, it is impossible using the nextUntil method. This is because of how jQuery performs traversing. According to the documentation,
Get all following siblings of each element up to but not including the element matched by the selector, DOM node, or jQuery object passed.
fifth is not a sibling of the clicked element, but rather it is a child of the sibling of the element's parents.
I came up with two possible solutions.
Solution 1: Combine NEXT and PREV traversals
Assuming that .clicked is always in the first list and .selected is always in the second list, combining prevAll() with nextAll() should do the trick. This assumes that the order is the same.
var siblings = $("li.clicked").nextAll()
Get all siblings of the current element AFTER the element itself.
var distantSiblings = $("li.selected").prevAll();
Get all distant siblings after the first element, but before the second one.
siblings.push(distantSiblings);
Combine them into two and then iterate over each element.
var siblings = $("li.clicked").nextAll()
var distantSiblings = $("li.selected").prevAll();
siblings.push(distantSiblings);
siblings.each(function() {
$(this).addClass("blue");
});
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
http://jsfiddle.net/r15z10o4/
Note:
You will notice that the above code works, however it might not be the optimal solution. This is only confirmed to work for your example above. There may also be a less verbose solution.
Solution 2 (Find index of all list items)
Another idea is to find the index of all items, and collect the elements that are sandwiched between those two indices. You will then want to use the 'slice' selector to get the range in between.
var items = $(".item");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = items.index(clicked);
var selectIndex = items.index(selected);
$("li").slice(clickIndex + 1, selectIndex).addClass("blue");
var clicked = $(".clicked");
var selected = $(".selected");
var clickIndex = $("li").index(clicked);
var selectIndex = $("li").index(selected);
$("li").slice(clickIndex+1, selectIndex).addClass("blue");
.blue { color: blue; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
You can do it manually by selecting all these items at once, and using loops.
Consider the parent element, let's say "container":
<div id="container">
<ul class="current">
<li class="item">first</li>
<li class="item clicked">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>
</div>
Now, you can select all these items:
var $items = $("#container > ul > li.item"); // or simply $("#container .item");
And iterate through them:
var $items = $(".item"), $result = $(), found = false;
for (var i = 0; i < $items.length; i++)
{
$currentItem = $items.eq(i);
if ($currentItem.is('.clicked')) {
found = true;
continue;
}
if ($currentItem.is('.selected'))
break;
if (found)
$result = $result.add($currentItem);
}
console.log($result);
Here is the working JSFiddle demo.
In any case it feels like you will need to define groups of li.
I think the easiest is to create a function getting a list of lis that you can request any way you want then to filter the el you are interested in.
function elRange(elList, start, end){
// we do not use indexOf directly as elList is likely to be a node list
// and not an array.
var startI = [].indexOf.call(elList, start);
var endI = [].indexOf.call(elList, end);
return [].slice.call(elList, startI, endI + 1);
}
// request the group of *ordered* elements that can be selected
var liList = document.querySelectorAll('ul.current > li, ul.later > li');
var selectionEnd = document.querySelector('.selected');
[].forEach.call(liList, function(li){
li.addEventListener('click', function(){
var selected = elRange(liList, li, selectionEnd);
selected.forEach(function(el){
el.classList.add('red');
})
});
});
.selected {
color: blue;
}
.red {
color: red;
}
<ul class="current">
<li class="item">first</li>
<li class="item">second</li>
<li class="item">third</li>
<li class="item">fourth</li>
</ul>
<ul class="later">
<li class="item">fifth</li>
<li class="item selected">sixth</li>
<li class="item">seventh</li>
</ul>

JS hiding a div while targeting anchor

I have multiple divs (class="profile") wich are hidden by default. Each div is only shown when targeted. I want all divs with class="employeeul" to be hidden when one of the profile divs is targeted. I don't get this working with css, does anyone know why? A JS solution is good as well. (I think I can't use something like onclick, because the divs must hide when the anchors are accessed from other sites.)
This is my code (I removed the divs content):
<div class="narrow_content">
<div class="profile" id="m_empfang0"></div>
<div class="profile" id="m_empfang1"></div>
<div class="profile" id="m_mitarbeiter0"></div>
<div class="profile" id="m_mitarbeiter1"></div>
<div class="profile" id="m_mitarbeiter2"></div>
<div class="profile" id="m_mitarbeiter3"></div>
<div class="profile" id="m_mieter0"></div>
<div class="profile" id="m_mieter1"></div>
<div class="profile" id="m_mieter2"></div>
<div class="employeeul">
<ul> <!-- Empfang -->
<li class="employee"></li>
<li class="employee"></li>
</ul>
</div>
<div class="employeeul">
<ul> <!-- Mitarbeiter -->
<li class="employee"></li>
<li class="employee"></li>
<li class="employee"></li>
<li class="employee"></li>
</ul>
</div>
<div class="employeeul">
<ul> <!-- Mieter -->
<li class="employee"></li>
<li class="employee"></li>
<li class="employee"></li>
</ul>
</div>
</div>
It seems like you just need the syntax for displaying/hiding items dynamically when the page has a specific url. In that case, here is a simple JS solution:
//get an array of elements with the class we're interested in working with
var employeeuls = document.getElementsByClassName("employeeul");
//get the current url
var url = window.location.href;
//if the current url is equal to example.php#profile, hide some elements
if(url == "example.php#profile")
{
//iterate over the array and apply the style to hide the elements
for(i=0; i < employeeuls.length; i++)
{
employeeuls[i].style.display = "none";
}
}
//otherwise, the elements should be hidden
else
{
//iterate over the array and apply the style to hide the elements
for(i=0; i < employeeuls.length; i++)
{
employeeuls[i].style.display = "block";
}
}
NOTE: "block" is the default display property for unordered lists.
I understand you're not using jQuery, but I'm going to include the jQuery equivalent for anyone viewing this post in the future:
//variable assigned to all elements with class "employeeul"
var employeeuls = $(".employeeul");
//get the current url
var url = $(location).attr("href");
//apply the style change
if(url == example.php#profile)
{
employeeuls.hide();
}
else employeeuls.show();
If by targeting, you mean the hash value in the URL, you just need to write some JS to grab that hash value and toggle the css. Then toggle show/hide (or a visibility class via jQuery).
$(document).ready(function(){
var $profiles = $('.profile'); // Store all the profiles in a query
var hashTarget = location.hash.replace('#', ''); // Returns hash value
function showTargetedDiv(){
$profiles.hide(); // Hide any divs that may previously be showing
$('#' + hashTarget).show();
}
showTargetedDiv();
$(window).on('hashchange', showTargetedDiv); // Event handler
});

How to delete an element which does not have a certain parent?

I want to delete list items who do not have a parent of ul
This is my HTML:
<div class="yamm-content">
<div class="row">
<li> Should be deleted
</li>
<li> Should be deleted
</li>
<ul id="randomid" class="col-xs-12 col-sm-4">
<li> menu item
</li>
<li> menu item
</li>
<li> menu item
</li>
</ul>
<ul id="randomid2" class="col-xs-12 col-sm-4">
<li> menu item
</li>
<li> menu item
</li>
<li> menu item
</li>
</ul>
</div>
</div>
and my current script:
if (!$('.nav .yamm-content row li').closest('ul').length == 0) {
$(this).remove();
}
jsFiddle
The first two list items don't have ul's as parents. So they should be deleted.
Simple: $('li:not(ul > li)').remove();
Demo http://jsfiddle.net/46NdQ/3/
$('li').filter(function(){return !$(this).closest('ul').length}).remove();
Or
$('li').not($('ul li')).remove();
Here i used Javascript only and it may take some lines of coding. I suggest this may be useful some times when you don't want to go for jquery.
var len = document.getElementsByTagName('li').length;
for (var i=0;i<len;i++)
{
if (document.getElementsByTagName('li')[i].parentNode.nodeName != "UL")
{
document.getElementsByTagName('li')[i].remove();
i--;
}
}
Here is the fiddle

Categories