I have an assignment that needs to be done in a very specific way. I have all the back end code done, but cant seem to figure this part out, and it's driving me mad. Essentially the UI is required to have 2 tree views one on the right and one on the left. The left tree view will be populated with states, and each state will have multiple children. The right will be empty to start. There will be two buttons. Include, and exclude. when children from the left view are selected, and then include is clicked they will move to the right tree. If the user decides to move them back they need to move to the same place they were under the same parent. Last, but not least, there can only be one of each child so we can't have duplicates when moving the children to new parents.
I can use any combination of html, css, javascript, and jquery.
here is what i have so far. I have tried many other things, but this is just the most recent.
CSS
#menutree li {
list-style: none; /* all list item li dots invisible */
}
li .menu_label + input[type=checkbox] {
opacity: 0; /* checkboxes invisible and use no space */
} /* display: none; is better but fails in ie8 */
li .menu_label {
cursor: pointer; /* cursor changes when you mouse over this class */
} /* could add the many user-select: none; commands here */
li .menu_label + input[type=checkbox] + ol > li
{
display: none; /* prevents sublists below unchecked labels from displaying */
}
li .menu_label + input[type=checkbox]:checked + ol > li
{
display: block; /* display submenu on click */
}
.selected {
background-color:#efefef;
}
jquery
$('#move_left').click(function() {
$('.list1').append($('.list2 .selected').removeClass('selected'));
});
$('#move_right').click(function() {
$('.list2').append($('.list1 .selected').removeClass('selected'));
});
$('body').on('click', 'li', function() {
$(this).toggleClass('selected');
});
HTML
<body>
<ol id="menutree">
<li>
<label class="menu_label" for="c1">Menu Gen14 Am0a1</label>
<input type="checkbox" id="c1" /> <!-- input must follow label for safari -->
<ol>
<li>
<ul class="list1">
<li class="page">Page Ap1a1</li>
<li> Page Ap1a2</li>
</ul>
</ol>
</ol>
</body>
<input type='button' value='<<' id='move_left'/>
<input type='button' value='>>' id='move_right'/>
Stupid example that does not take children ordering under consideration, but logic that you presented should be preserved
Demo
<div class="half">
<ol id="one">
<li id="alaska">
Alaska
<ul>
<li data-id="alaska"><input type="checkbox"> Children 1</li>
<li data-id="alaska"><input type="checkbox"> Children 2</li>
<li data-id="alaska"><input type="checkbox"> Children 3</li>
</ul>
</li>
<li id="washington">
Washington
<ul>
<li data-id="washington"><input type="checkbox"> Children 1</li>
<li data-id="washington"><input type="checkbox"> Children 2</li>
<li data-id="washington"><input type="checkbox"> Children 3</li>
</ul>
</li>
<li id="texas">
Texas
<ul>
<li data-id="texas"><input type="checkbox"> Children 1</li>
<li data-id="texas"><input type="checkbox"> Children 2</li>
<li data-id="texas"><input type="checkbox"> Children 3</li>
</ul>
</li>
</ol>
<button type="button" class="include">Include</button>
</div>
<div class="half">
<ol id="two"></ol>
<button type="button" class="exclude">Exclude</button>
</div>
$(function(){
$('.include').click(function(e){
var $checks = $("input:checked");
$.each($checks, function(k,v){
var tempid = $(this).parent().data('id');
if( !$('#two').find('[data-id="'+tempid+'"]').length ){
var element = $(this).parent().detach();
$('#two').append(element);
}
});
});
$('.exclude').click(function(e){
var $checks = $("input:checked");
$.each($checks, function(k,v){
var tempid = $(this).parent().data('id');
var element = $(this).parent().detach();
$('#one').find('#'+tempid+' ul').append(element);
});
});
});
Related
I am new in Jquery
My Webpage structure is like this
<div id="MenuSection">
<ul>
<li>Master // Main Menu
<ul>
<li>Master1</li>
<li>Master2</li>
<li>Master3</li>
</ul>
</li>
<li>Transaction // Main Menu
<ul>
<li>Transaction1</li>
<li>Transaction2</li>
<li>Transaction3</li>
</ul>
</li>
<li>Report // Main Menu
<ul>
<li>Report1</li>
<li>Report2</li>
<li>Report3</li>
</ul>
</li>
</ul>
</div>
I want that when all the children of any Parent(main menu) are hidden, Parent should also be hidden. Let's say if Report1, Report2, Report3 are hidden then Parent that is "Report" should also be hidden.
How can I achieve this through Jquery ?
One way is to iterate over each main menu li to see if its children are all :visible:
$("#MenuSection>ul>li").each(function() {
if ($(this).find(">ul>li:visible").length == 0) {
$(this).hide();
}
});
there are other ways to do this, such as using .filter or .map, but this should get you what you need.
Given the nested ul's the above uses > to ensure only the directly ul>li children are processed. If you have multiple levels, you might need to change accordingly, eg for the first: #MenuSection li would apply to all lis and the second .find(">ul>li:visible") only looks at direct li children.
$("#MenuSection>ul>li").each(function() {
if ($(this).find("li:visible").length == 0) {
$(this).hide();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="MenuSection">
<ul>
<li>Master
<ul>
<li>Master1</li>
<li>Master2</li>
<li>Master3</li>
</ul>
</li>
<li>Transaction
<ul>
<li>Transaction1</li>
<li>Transaction2</li>
<li>Transaction3</li>
</ul>
</li>
<li>Report
<ul>
<li style='display:none'>Report1</li>
<li style='display:none'>Report2</li>
<li style='display:none'>Report3</li>
</ul>
</li>
</ul>
</div>
JavaScript does it fairly easy. You would need expand on this to execute this check every on the relevant list every time you hide or show a list item.
function isHidden(array) {
for(var i = 0; i < array.length - 1; i++) {
if(array[i+1].style.display != "none") {
return false;
};
};
return true;
};
var children = document.getElementById("report").getElementsByTagName("LI");
if (isHidden(children)) {
document.getElementById("report").style.display = "none";
};
You can use the the .is(':visible')
See the code:
(function($) {
$(document).ready(function() {
var $mainLinks = $('#MenuSection > ul > li');
$.each($mainLinks, function() {
if (!$(this).find('li').is(':visible')) {
$(this).hide();
}
});
});
})(jQuery);
#MenuSection > ul > li:last-child li {
display: none;
}
#MenuSection > ul > li:first-child li:first-child {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div id="MenuSection">
<ul>
<li>Master // Main Menu
<ul>
<li>Master1</li>
<li>Master2</li>
<li>Master3</li>
</ul>
</li>
<li>Transaction // Main Menu
<ul>
<li>Transaction1</li>
<li>Transaction2</li>
<li>Transaction3</li>
</ul>
</li>
<li>Report // Main Menu
<ul>
<li>Report1</li>
<li>Report2</li>
<li>Report3</li>
</ul>
</li>
</ul>
</div>
I'm creating a navbar for a website and I need a very simple way of creating the drop-downs for 2 levels going from the main bar to a drop-down list with a further drop-down level.
I'm using JQuery with no extra plugins.
By "Simplest", I mean with the fewest lines of code and the fewest variables and so the fastest loading time.
I have tried using the hover() event but this does not seem to work.
Here is an example of the HTML:
<div class="menu">
<ul>
<li>Link 1</li>
<li>Link 2
<ul class="sub">
<li>Sub 1
<ul class="sub2">
<li>Sub 2</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Ideally, hovering over "Link 2" will have the child list slide down, and hovering over "Sub 1" will have the child list slide down next to it.
Also, I'm using percentages for most of the dimensions so it would be really useful if this scaled the same way.
If any more information is needed then ask before down-voting please.
Thanks.
The best jQuery is no jQuery.
.menu input {
position: absolute;
left: -9999px;
}
.menu label {
cursor: pointer;
display: block;
}
.menu label:after {
content: '...';
}
.sub {
display: none;
}
.menu label:hover+input+.sub,
.menu input:checked+.sub,
.sub:hover {
display: block;
}
<div class="menu">
<ul>
<li>Link 1</li>
<li>
<label for="menu-2">Link 2</label>
<input type="checkbox" id="menu-2" />
<ul class="sub">
<li>
<label for="menu-2-1">Sub 1</label>
<input type="checkbox" id="menu-2-1" />
<ul class="sub">
<li>Sub 2</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
Add further CSS as necessary.
I'm trying to write a function in jquery that will loop through a ul with nested ul's inside of it. I'd like it to keep track of how many ul's or "layers" in I am, and give a class label to the li children of a ul that correspond to the "layer" that their parent ul is on.
The structure of the html is such`
<div class="region region--megamenu">
<div class="block block--services-menu">
<ul class="menu">
<li class="first last expanded active-trail">
Services
<ul class="menu"> <!-- This ul can have any number of metacategory li's inside of it -->
<li class="second layer of li">
Metacategory Pages
<ul class="menu"> <!-- This ul can have any number of ul's inside of it -->
<li>
</li>
<li>
</li>
</ul>
</li>
<li>
Metacategory Pages
<ul class="menu"> <!-- This ul can have any number of ul's inside of it -->
<li>
</li>
<li>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
Each li would get a number as a new class label.
So the first li element would get a label <li class="1 first layer of li">, and the second layer of li would be labeled <li class="2 second layer of li">
In plain JS you can use a recursive function (though a linear one will be faster). This goes down the DOM and adds a class to each LI. When called recursively on an LI, it increments the layer count for that call.
Not sure it fits your purpose, but might be close enough. You could also get the ULs, then add a layer-specific class to their immediate LI children.
The function below could be optimised to skip non–empty elements that won't have LI children (e.g. script elements) if required.
<style type="text/css">
.foo-0 {
background-color: red;
}
.foo-1 {
background-color: blue;
}
.foo-2 {
background-color: green;
}
</style>
<div id="d0">
<ul>
<li>layer 1
<li>layer 1
<ul>
<li>layer 2
<li>layer 2
<ul>
<li>layer 3
<li>layer 3
</ul>
<li>layer 2
</ul>
<li>layer 1
<li>layer 1
</ul>
</div>
<script>
/*
** #param {DOM element} root - element to start from, default is document.body
** #param {string} classPre - prefix for class to add
** #param {number} layer - current level, default is 0
*/
function addULLayerClass(root, classPre, layer) {
root = root || document.body;
layer = layer || 0;
var node, nodes = root.childNodes;
var tagName;
for (var i=0, iLen=nodes.length; i<iLen; i++) {
node = nodes[i];
tagName = node.tagName && node.tagName.toLowerCase();
// If this is an LI, add class with layer number
// and call recursively
if (tagName == 'li') {
node.className = classPre + '-' + layer;
addULLayerClass(node, classPre, layer + 1);
// Otherwise, if it's an element that can have children,
// call recursively
} else if (node.nodeType == 1) {
addULLayerClass(node, classPre, layer);
}
}
}
addULLayerClass(document.getElementById('d0'), 'foo');
</script>
You can easily accomplish this with the JQuery .find() method.
HTML
<div class="region region--megamenu">
<div class="block block--services-menu">
<ul class="menu">
<li class="first last expanded active-trail">
Services
<ul class="menu"> <!-- This ul can have any number of metacategory li's inside of it -->
<li class="second layer of li">
Metacategory Pages
<ul class="menu"> <!-- This ul can have any number of ul's inside of it -->
<li>
</li>
<li>
</li>
</ul>
</li>
<li>
Metacategory Pages
<ul class="menu"> <!-- This ul can have any number of ul's inside of it -->
<li>
</li>
<li>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div id="info"></div>
JS/JQUERY
var allListElements = $( "ul" );
var ulsInUl = $( "ul.menu" ).find( allListElements ).length;
document.getElementById("info").innerHTML = "Uls in UL Class main: " + ulsInUl;
Check out this JSFiddle: http://jsfiddle.net/aaaprbst/1/
Here is the code that I implemented which does exactly what I wanted. Thanks everyone for your contributions.
var parentUL = $( "ul.menu" );
var startSearchForParentsHere = ".block--services-menu";
$( startSearchForParentsHere ).find( parentUL ).each(function(){
layersDeep = $(this).parentsUntil(startSearchForParentsHere, parentUL).length;
$(this).children().addClass('accordion-layer' + ' ' + layersDeep);
})
Further feedback and other creative solutions are also greatly appreciated!
Actually,I have a multiselect option to select,But now i can able to select a parent & there child alone,But now i want to select a Group With its parent & Child,
So if i select a group all the parent & child in the group should have be moved with the group,So help me....
Here is my example code
<script src="./jquery.min.js"></script>
<script type="text/javascript">
var me = jQuery || $.noConflict();
me(document).ready(function() {
if((me('ul li ul li ul li').children().length) == 0 ) {
me('ul li ul li ul li').addClass("list");
}
me('ul li ul li').click(function() {
me('ul li ul li').removeClass("list_state");
if((me(this).children().length) > 0 ) {
me(this).addClass("list_state");
me('.list_state').click(function() {
$val = me(this).prop("tagName").toLowerCase();
$txt = me(this).text();
me('#result').html($txt).wrapInner('<'+$val+'>');
});
}
});
me('li.list').click(function() {
$val = me(this).prop("tagName").toLowerCase();
$txt = me(this).text();
me('#result').html($txt).wrapInner('<'+$val+'>');
});
});
</script>
<ul>
<li id="level-0">India
<ul>
<li id="level-0-3">Tamil nadu
<ul>
<li>Chennai</li>
<li>Trichy</li>
<li>Madurai</li>
<li>Kanya kuamri</li>
</ul>
</li>
</ul>
</li>
<li id="level-1">United Kingdom
<ul>
<li id="london">London
<ul>
<li>London1</li>
<li>London2</li>
<li>London3</li>
<li>London4</li>
</ul>
</li>
</ul>
</li>
</ul>
<div id="result"></div>
If i select a group (i.e) India then all the childs under this group should be moved to right column as like jquery ui multiselect option
I have attached an example screenshot below..
Check this fiddle
In the above fiddle only the text within clicked li and its successor li's will be displayed:
jQuery
$('ul').on('click','li',function(){
$('#result').html($(this).text());
return false;
});
Updated code in response to comment
HTML(slight change added class="par" to Country and State)
<ul>
<li id="level-0" class="par">India
<ul>
<li id="level-0-3" class="par">Tamil nadu
<ul>
<li>Chennai</li>
<li>Trichy</li>
<li>Madurai</li>
<li>Kanya kuamri</li>
</ul>
</li>
</ul>
</li>
<li id="level-1" class="par">United Kingdom
<ul>
<li id="london" class="par">London
<ul>
<li>London1</li>
<li>London2</li>
<li>London3</li>
<li>London4</li>
</ul>
</li>
</ul>
</li>
</ul>
<div id="result"></div>
Updated JQuery
$('ul').on('click','li',function(){
$('#result').html($(this).html());
if($(this).attr('class')=="par")
{
return false;
}
});
Added little CSS for look.(Do CSS change as needed)
html,body{
margin:0px;
padding:0px;
width:100%;
}
ul{
width:50%;
display:table-cell;
border:1px solid black;
}
div{
width:50%;
display:table-cell;
border:1px solid black;
}
Ok, I have a set of checkboxes for selecting criteria. For argument's sake, we'll say the data looks like this:
[] Vehicles
[] Unpowered
[] Bicycle
[] Skateboard
[] Powered
[] Two-wheeled
[] Motorcycle
[] Scooter
[] Four-wheeled
etc
The []s represent checkboxes.
Ignoring the obviously contrived nature of this example, the idea is this:
To start with, only the Vehicle checkbox is visible;
If the user clicks on the Vehicle checkbox is opsn up the next level (Powered, Unpowered);
If the user selects Powered it opens up the next level (Two-wheeled, Four-wheeled);
If the user then unchecks Powered, that level disappears.
Now this is relatively easy to set up with onclick's toggling the display CSS attribute between block and none.
This is currently structured on the page as:
<table>
<tr>
<td><input type="checkbox" onclick="toggle('__Vehicles');"></td>
<td>Vehicles
<table id="__Vehicles">
<tr>
<td><input type="checkbox"></td>
<td>Unpowered
etc
I should point out before someone asks: the reason the checkbox was put in table cell was to control formatting. It made it easy to effectively indent since everything in the next table cell would line up.
It all works fine but the table nesting gets pretty deep. I keep thinking there has to be a better way than this. It has to be able to be easily built dynamically and have good cross-browser support for formatting of the "tree".
I should also mention that jQuery is available. I'm using it for other things.
Suggestions?
Edit: Yes the checkbox styling is important as a couple of comments have noted. Also, I have posted a solution to this, based on the responses I've gotten, as an answer below (too big to add here), just for those curious to see an example.
<ul>
<li><input type="checkbox" />Vehicles <ul>
<li><input type="checkbox" />Unpowered</li>
<li><input type="checkbox" />Bicycle</li>
<li><input type="checkbox" />Skateboard</li>
</ul></li>
<li><input type="checkbox" />Powered <ul>
<li><input type="checkbox" />Two-wheeled <ul>
<li><input type="checkbox" />Motorcycle</li>
<li><input type="checkbox" />Scooter</li>
</ul></li>
<li><input type="checkbox" />Four-wheeled</li>
</ul></li>
</ul>
Edit: a little css & js to show & hide nested elements (no checkboxes)
li.opened ul {
display: block;
}
li.closed ul {
display: none;
}
and js...
$(document).ready(function() {
$('li input:checkbox').click(function () {
$(this).parent().toggleClass('opened');
$(this).parent().toggleClass('closed');
});
$('li').addClass('closed');
});
edit, again, because Sparr wants some better styles (assuming that the checkboxes have a style of "checkbox"
li input.checkbox { /* input:checkbox is not 100% compatible */
width: 6px;
margin: 0 2px;
/* This makes 10px be the total "width" ofh the checkbox */
}
ul {
margin: 0 0 0 10px; /* Or whatever your total checkbox width is */
padding: 0;
}
li {
padding: 0;
}
You could do this:
<ul>
<li>
<input type="checkbox" /> Option 1
<ul>
<li><input type="checkbox" /> Option 1 Sub Option A</li>
</ul>
</li>
</ul>
You'd then set the padding/margin of the UL's to 0 and 0. Then set the padding-left of the LI's to 10px.
ul {
margin:0;
padding:0;
}
li {
margin:0;
padding:0 0 0 20px; /* Each nested li will be padded incrementally */
}
For the javascript, attach an event to each checkbox that determines whether the sibling UL (if any exists) should be visible. If the box is checked, show it, else, hide it.
Nested unordered lists are best practice for this sort of thing.
<ul>
<li>Item 1</li>
<li>Item 2
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
</ul>
</li>
<li>Item 3</li>
<li>Item 4
<ul>
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
</ul>
</li>
</ul>
Wanna see some deep jQuery magic?
<ul class="tree">
<li><input type="checkbox" name="Vehicles" checked>Vehicles
<ul>
<li<input type="checkbox" name="Unpowered">Unpowered
<ul>
<li><input type="checkbox" name="Bicycle">Bicycle</li>
<li><input type="checkbox" name="Skateboard">Skateboard</li>
</ul>
</li>
<li><input type="checkbox" name="Powered" checked>Powered
<ul>
<li><input type="checkbox" name="Two-wheeled">Two-wheeled
<ul>
<li><input type="checkbox" name="Motorcycle" checked>Motorcycle</li>
<li><input type="checkbox" name="Scooter">Scooter</li>
</ul>
</li>
<li><input type="checkbox" name="Two-Wheeled">Four-wheeled</li>
</ul>
</ul>
</li>
</ul>
Note: the only decoration here is the tree class.
ul.tree {
list-style-type: none;
margin: 0 0 0 -22px;
padding: 0;
}
ul.tree ul {
list-style-type: none;
margin: 0;
padding: 0;
}
ul.tree input {
margin-right: 6px;
}
ul.tree li {
padding: 0 0 0 22px;
margin: 1px;
}
.closed ul {
display: none;
}
and the magic:
$(function() {
$("ul.tree li:has(ul) > :checkbox").click(function() {
jQuery(this).parent().toggleClass('closed');
}).not(":checked").parent().addClass("closed");
});
That turns the entire thing into a working opening and closing tree as you click on checkboxes. Awesome.
Thanks to davethegr8, Jonathon Sampson and others for advice.
I have to chime in to suggest that you extract the javascript out from your markup, in addition to the suggestions above. Using a library such as lowpro (a favorite of mine), you can create 1 object to handle your nested checkbox behavior, and have it automatically applied, unobtrusively. Packaging up your code like this makes your markup easier to maintain, and your code easier and quicker to write, more powerful, and more easily maintained.
Tables are for tabular data. Use nested lists instead and CSS for formatting.
If you're looking for a complete solution, here's one using pure CSS for modern browsers and JavaScript for IE:
<style>
ul.tree, ul.tree ul {
position: relative;
list-style: none;
margin: 0 0 0 20px;
padding: 0;
}
ul.tree input {
position: absolute;
margin-left: -20px;
}
ul.tree ul {
display: none;
}
ul.tree input:checked ~ ul {
display: block;
}
ul.tree label:hover {
text-decoration: underline;
}
</style>
<ul class="tree">
<li>
<input type="checkbox" id="option1">
<label for="option1">Option 1</label>
<ul>
<li>
<input type="checkbox" id="option1a">
<label for="option1a">Option 1 Sub Option A</label>
</li>
<li>
<input type="checkbox" id="option1b">
<label for="option1b">Option 1 Sub Option B</label>
</li>
</ul>
</li>
<li>
<input type="checkbox" id="option2">
<label for="option2">Option 2</label>
</li>
</ul>
<!--[if lte IE 7]>
<script>
var tree = document.getElementsByTagName('ul')[0];
tree.attachEvent('onclick', function() {
var src = event.srcElement;
if(src.nodeName.toLowerCase() === 'label')
var box = document.getElementById(src.htmlFor);
else if(src.nodeName.toLowerCase() === 'input')
var box = src;
else return;
for(var current = src.nextSibling;
current && current.nodeName.toLowerCase() !== 'ul';
current = current.nextSibling);
if(current)
current.style.display = box.checked ? 'block' : 'none';
});
</script>
<![endif]-->
Assumes that a checkbox is not wider that 20px.
While the jQuery plugin mcDropdown approaches the nested list problem in a different way (no checkboxes), it may be suitable for your needs.
when creating cb's give them a css class depending on the level
<input class="LevelXcb" type="checkbox" />
CSS:
.Level0cb{left:4px;}
.Level1cb{left:16px;}
.Level2cb{left:28px;}
if "left" does not work, try setting "margin-left".
I know this isn't much related to the question, so pls don't up vote is just a sugestion.
In this example from davethegr8:
<ul>
<li>Vehicles <ul>
<li>Unpowered</li>
<li>Bicycle</li>
<li>Skateboard</li>
</ul></li>
<li>Powered <ul>
<li>Two-wheeled <ul>
<li>Motorcycle</li>
<li>Scooter</li>
</ul></li>
<li>Four-wheeled</li>
</ul></li>
</ul>
If you give each step a class, you can use jquery to make some cool drop down efects with on click:
Like
$("li.Two-wheeled").click(function(event){
$("div.main_wrapper ul.Moto_or_scooter:hidden").slideDown("slow");
}
Just a small sugestion though :)
If the width of the checkboxes is still an issue (after all previous answers...) you can either:
Put all checkboxes in a fixed with box so you know exactly where the text is beginning (and the next level checkbox)
Use some jquery calculations to get the width and set the margins of the other elements dynamically (seems a bit much though...)
Using the suggested nested list structure obviously.