I'm creating a portfolio site, and I'm struggling to select the proper elements with JQuery. My goal is to show/hide the .inner (Task) items when clicking the .outer (Category) items. I have .arrows that rotate when the menu is expanded.
This is a similar question, and the accompanying jsfiddle.
Thanks to Tats_innit for the original answer.
I have this HTML:
<li class="outer" hook="01">
<div class="arrow"></div>
Category 1
</li>
<li class="inner" id="menu-01">
Task 1
</li>
<li class="inner" id="menu-01">
Task 2
</li>
<li class="inner" id="menu-01">
Task 3
</li>
<li class="outer" hook="02">
<div class="arrow"></div>
Category 2
</li>
<li class="inner" id="menu-02">
Task 1
</li>
<li class="inner" id="menu-02">
Task 2
</li>
<li class="inner" id="menu-02">
Task 3
</li>
And this jquery:
$(document).ready(function(){
$('.outer').click(function(){
var elem = $('#menu-'+$(this).attr('hook')),
arrow = $(this).children('.arrow')
if (!elem.is(':visible')) {
arrow.rotate({animateTo:90, duration:128});
} else {
arrow.rotate({animateTo:0, duration:128});
}
elem.slideToggle('128ms', function() {
});
return false;
});
});
I understand that I need to change var elem = $('#menu-'+$(this).attr('hook')) but I'm not sure how to display all instances of .inner.
I didn't nest the .inner elements because I have a hover state background-color: #f1f1f1; for the .outer class.
This problem can be solved, but there are many things wrong with your approach in the first place, so I would like to aim to change those, instead. And then solve the problem.
The id attribute is unique. You cannot have multiple elements with the same id in the same page; for that reason, use class.
hook is not a valid HTML attribute; while it is true that this won't hurt you and browsers will most likely ignore it, I would rather see it standardized as data-hook if you so wish. This will allow you also to use the standard APIs.
You should nest the .inner inside the .outer because it makes sense semantically and it will degrade gracefully and be understandable to screen readers as well. The problem you mention about the background on hover, I think could be easily solved with some good CSS: though I didn't understand the specific issue, so I cannot say.
But fact is, that using nesting you probably don't need most of those ids, classes and random data attributes (unless obviously you need them for something else than just opening up a list).
After that you HTML will look like this:
<ul>
<li class="outer">
<div class="arrow"></div>
Category 1
<ul>
<li>
Task 1
</li>
<li>
Task 2
</li>
<li>
Task 3
</li>
</ul>
</li>
<li class="outer">
<div class="arrow"></div>
Category 1
<ul>
<li>
Task 1
</li>
<li>
Task 2
</li>
<li>
Task 3
</li>
</ul>
</li>
</ul>
And you jQuery like so:
$('.outer').click(function(){
var elem = $(this).children('ul'),
arrow = $(this).children('.arrow')
if (!elem.is(':visible')) {
arrow.rotate({animateTo:90, duration:128});
} else {
arrow.rotate({animateTo:0, duration:128});
}
elem.slideToggle('128ms', function() {
});
return false;
});
As a final note, .arrow is not a very semantic element, since it's not even used to activate the animation (the click is bound the the li.outer) I would remove it altogether and instead achieve the same effect using a pseudo-element on the said li, and perhaps rotate it with CSS3.
Edit
I am not sure why you were not able to make it function properly; though you tell me that you appreciated the comment regarding the nesting, your fiddle didn't use the correct nesting.
In any case, I put up an example for you with the advice I gave you here, and basically I just copied the code verbatim as I wrote it here. I made a few adjustments, though: such as implementing the CSS pseudoelement and getting rid of that div.arrow element, which honestly looked bad.
Obviously is just an example, but it shows how think this problem should be approached. Hopefully it will solve your doubts:
Working example
You can nest a set of outer and inner into a div
<div>
<li class="outer">
<div class="arrow"></div>
Category 1
</li>
<li class="inner">
Task 1
</li>
<li class="inner">
Task 2
</li>
<li class="inner">
Task 3
</li>
</div>
then your js code goes like
$('.outer').click(function(){
var elem = $(this).siblings('.inner'),
arrow = $(this).children('.arrow')
if (!elem.is(':visible')) {
arrow.rotate({animateTo:90, duration:128});
} else {
arrow.rotate({animateTo:0, duration:128});
}
elem.slideToggle('128ms', function() {
});
return false;
});
won't affect your background-color for outer class in the meantime
<script>
$(document).ready(function(){
$('li.inner').hide();
$('.outer').click(function()
{
var elem = $(this).attr('hook');
$('li.inner').hide();
$(".menu-"+elem).toggle();
});
});
</script>
<ul>
<li class="outer" hook="01">
<div class="arrow"></div>
Category 1
</li>
<li class="inner menu-01">
Task 1
</li>
<li class="inner menu-01">
Task 2
</li>
<li class="inner menu-01">
Task 3
</li>
<li class="outer" hook="02">
<div class="arrow"></div>
Category 2
</li>
<li class="inner menu-02">
Task 1
</li>
<li class="inner menu-02">
Task 2
</li>
<li class="inner menu-02">
Task 3
</li>
</ul>
first of all change li id to class because 2 li don't have same id
I guess you are looking for nextUntil()
$(this).nextUntil('.outer').slideToggle('218ms');
http://jsfiddle.net/bf7Ke/53/
Related
I am struggling with jquery a little. I have an unordered list that looks like this.
<ul>
<li class="folder">Folder: Test</li>
<ul>
<li class="folder">Folder: Archive</li>
<ul>
<li class="file">
<div class="filename">HelloWorld.docx</div>
<div class="size">11.79kiB</div>
<div class="date">2021-01-12 09:31:34</div>
</li>
<li class="file">
<div class="filename">HelloWorld1.docx</div>
<div class="size">12.79kiB</div>
<div class="date">2021-01-11 09:31:34</div>
</li>
</ul>
</ul>
</ul>
Which looks like this
Folder: Test
Folder : Archive
HelloWorld.docx
11.79kiB
2021-01-12 09:31:34
HelloWorld1.docx
12.79kiB
2021-01-11 09:31:34
When I click on any of the li's with the class of "file" I want to look back and work out what the path structure is by finding the parent li's that have the class "folder".
I have tried various combinations but cannot get it
This is what I am working with at the moment
$(document.body).on('click',"li.file",function (e) {
console.log("clicked");
$(this).parents("li.folder").each(function() {
console.log($(this).text());
});
});
Ultimately i want to get back a full path with the parent folder and the filename in a variable.
e.g. pathtofile = /Test/Archive/HelloWorld.docx
Here is a jsfiddle https://jsfiddle.net/e5d7bcyz/
Thanks
Before approaching your question you first need to correct the HTML. ul elements cannot be children of other ul elements. You need to wrap the ul within their associated li.
You will also need to wrap the folder names in another element, such as a span, in order for the text to be easily retrievable. This would be possible with your current HTML by trawling through text nodes, however that is messy code to write and very brittle. A simple HTML change is the best approach there.
Finally, you can loop through the parent li elements of the clicked .file and reverse their order to get the path in the right format. From there you can append the filename of the selected file. Try this:
$.fn.reverse = [].reverse;
$(document).on('click', "li.file", function(e) {
let $file = $(this);
let $path = $file.parent().parents('li').reverse();
let path = $path.map((i, el) => $(el).children('span').text().trim()).get();
path.push($file.children('.filename').text().trim());
console.log(path.join('/'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li class="folder">
<span>Test</span>
<ul>
<li class="folder">
<span>Archive</span>
<ul>
<li class="file">
<div class="filename">HelloWorld.docs</div>
<div class="size">11.79kiB</div>
<div class="date">2021-01-12 09:31:34</div>
</li>
<li class="file">
<div class="filename">HelloWorld1.docs</div>
<div class="size">12.79kiB</div>
<div class="date">2021-01-11 09:31:34</div>
</li>
</ul>
</li>
</ul>
</li>
</ul>
Well, the LI you're looking for are not really the parents as they're not wrapping the current li.file element.
<ul>
<li class="folder">Folder: Archive</li> // You close the LI tag. So it's not a parent of the rest of the code.
<ul>
<li class="file">
Try to wrap the rest of the code with the LI tag:
<ul>
<li class="folder">Folder: Test
<ul>
<li class="folder">Folder: Archive
<ul>
<li class="file">
<div class="filename">HelloWorld.docx</div>
<div class="size">11.79kiB</div>
<div class="date">2021-01-12 09:31:34</div>
</li>
<li class="file">
<div class="filename">HelloWorld1.docx</div>
<div class="size">12.79kiB</div>
<div class="date">2021-01-11 09:31:34</div>
</li>
</ul>
</li> //closing the second parent: Folder: Archive
</ul>
</li> //closing the first parent: Folder: test
</ul>
As far as I know it's valid HTML code.
And then those li.folder elements would be actually parents.
I want to move some span elements to closest div. I found the solution to my problem but it does not work for me. I have some Html code:
<ul>
<li>
<a><span>Some info</span></a>
</li>
<div class="cl1">...</div>
<li>
<a><span>Some info 2</span><a>
</li>
<div class="cl1">
...
</div>
...
</ul>
and to move <span> like this:
$('span').each(function () {
$(this).parent().parent().closest('.cl1').append(this);
})
but nothing happened. Any help would certainly be appreciated
you can't put a div in a ul, only li's.
your html has to be valid (a's, ul need to be closed)
Closest searches anscetors, not siblings.
since your markup is not valid as is, i'm not sure if you want the divs in the list or not. This example removes them from the lis, which breaks the list into two lists.
$('button').click(function() {
$('span').each(function() {
var $div = $(this).closest('ul').siblings('.cl1');
$(this).clone().appendTo($div);
$(this).remove();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>
<a><span>Some info</span></a>
</li>
</ul>
<div class="cl1">...</div>
<ul>
<li>
<a><span>Some info 2</span></a>
</li>
</ul>
<div class="cl1">...</div>
<button>Do Stuff</button>
I have a very annoying problem, which might be a product of my poor knowledge of javascript and jQuery.
I have a list that uses recursion to enable a hierarchy-structure, it looks as follows
$(function (){
$('#foo').click(function() {
$(this).children('ul').slideToggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id='foo'>A
<ul>
<li id='foo'>B
<ul>
<li>
Sub-sub
</li>
</ul>
</li>
</ul>
</li>
</ul>
I'm trying to accomplish a collapse function, so that when the user clicks on 'A' all the children elements collapses, and if she clicks the 'B' node all of 'B's children collapses. But however I try I always end up having all of the lists with id = 'foo' collapsing.
In my eyes, $(this).children('ul').slideToggle(); will collapse the children, since $(this) points to the list element clicked...?
Been at this for far to long now, would love some help!
Here you go... No change in HTML. But like other suggested, you need to have unique ID's
$(function (){
$('li').click(function(event) {
event.stopPropagation();
$(event.target).children('ul').slideToggle();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li id='foo'>A
<ul>
<li id='foo'>B
<ul>
<li>
Sub-sub
</li>
</ul>
</li>
</ul>
</li>
</ul>
I have a nested set of objects I would like to sort. The first example, directly below, is my prototype / test. It works nicely. The outer groups sort, and the inner groups sort. the inner groups are locked inside - cant be moved outside. Just exactly like i want...
<div id="example5">
<ul class="table">
<li class="group" style="width:300px;">Group 1
<ul style="padding:0px;">
<li class="field">Group 2</li>
<li class="field">Group 3</li>
</ul>
</li>
<li class="group">Group 2</li>
<li class="group">Group 3</li>
<li class="group">Group 4</li>
</ul>
</div>
And
$("#example5 ul").sortable({
});
This is the same structure I am trying to impart in my program. Everything seems the same to me, just with some added complexity. Clearly, I am breaking it somehow. The outer groups sort, while the inner ones do not.
I think its important to note that in the previous example, the script and html are in the same file. In the second example, below, the html is dynamically created with createElement() function
<div id="logtable">
<ul id="ultable" class="table ui-sortable">
<li id="" class="groupcontainer" style="width:auto; margin:2px;">
<ul>
<li class="fieldcontainer">
<div class="fieldname"></div>
<div class="fieldgroup"></div>
<div class="fieldname"></div>
<div class="fieldname"></div>
<div class="fieldname"></div>
</li>
</ul>
</li>
<li id="a" class="groupcontainer" style="margin: 2px;"></li>
<li class="groupcontainer" style="margin: 2px;"></li>
</ul>
</div>
Javascript:
$(function() {
var fieldstart;
var fieldend;
$("#logtable ul").sortable({
start: function(event, ui) {
fieldstart = ui.item.index();
},
update: function(event, ui) {
fieldend = ui.item.index();
var fieldcount = jsonstring.tracelog.fields.length;
var fieldobjects = jsonstring.tracelog.fields;
var placed = false;
jsonstring.tracelog.fields.move(fieldstart, fieldend);
writejson();
}
});
});
Following up, this site... http://www.trace-log.com/sortworks.php is copied from the "inspect element" output. it works exactly like i want. http://www.trace-log.com is the site i generated it from. basically, add a value to the field name, it will generate a new field. entering a value in the first text box will initiate the "grouping" of fields. cant understand why this isn't working.
so as it stands, it works if i statically recreate the page, but doesnt work correctly on the dynamically created page. could it be somehow with the way the sortable function is being called?
In the second example the field container is an li and the field names are div tags. The first example you had the field container as an ul tag and the fields as li. I think this might be your issue.
see fiddle here http://jsfiddle.net/4Mk4K/3/
You can try adding a custom class to the elements you want to be sortable and then use that class in the items option of sortable.
Demo:http://jsfiddle.net/lotusgodkk/GCu2D/161/
HTML:
<div id="logtable">
<ul id="ultable" class="table ui-sortable">
<li id="" class="groupcontainer item" style="width:auto; margin:2px;">Z
<ul>
<li class="fieldcontainer">
<div class="fieldname item">A</div>
<div class="fieldgroup item">B</div>
<div class="fieldname item">C</div>
<div class="fieldname item">D</div>
<div class="fieldname item">E</div>
</li>
</ul>
</li>
<li id="a" class="groupcontainer item" style="margin: 2px;">F</li>
<li class="groupcontainer item" style="margin: 2px;">G</li>
</ul>
</div>
JS:
$(function () {
var fieldstart;
var fieldend;
$("#logtable ul").sortable({
items: '.item',//Custom class of items which needs to be sorted.
start: function (event, ui) {
fieldstart = ui.item.index();
},
});
});
After much fiddling and fussing, relocating the sortable function into the function that creates the DOM objects solved the problem. I think the way it was originally structured, some DOM objects were created AFTER the sortable function - and sortable didn't know they existed.
So yes, there was something else structurally wrong with my code. Thanks everyone for the help.
I have one Activity xml file and I am try to get from activity when click on activity there child display. Its look like end of the all click.
<ul id="firstLevelChild">
<ul id="ul">
<li id="4">Activities
<ul class="ul">
<li id="10066">Physical1
<ul class="ul">
<li id="10067">Cricket
<ul class="ul">
<li id="10068">One Day</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</ul>
Now I want that if li have no leaf node then its display in other another div. Something like:
Click on Acitivities there have child node Physical1 and there also child Cricket and there chil One Day now one day have no child when click on one day its display in my <div id="result"></div>
I would add this as a comment, but I don't have enough rep. ChildNodes() isn't a function - since it looks like you're using jQuery, try children() instead.
I think javascript could helpr you there. A part from the fact that you first build your DOM correct ;)
The hasChildNodes() method returns TRUE if the current element node has child nodes, and FALSE otherwise.
http://www.w3schools.com/dom/met_element_haschildnodes.asp
Assuming the markup you provided is how it's going to be always i.e. ul as child for all li. You just check if ul exists inside the current li. See fiddle
HTML
<div id="content">
<ul id="firstLevelChild">
<li>
<ul id="ul">
<li id="4">Activities
<ul class="ul">
<li id="10066">Physical1
<ul class="ul">
<li id="10067">Cricket
<ul class="ul">
<li id="10068">One Day</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<h2>Result</h2>
<ul id="result"></ul>
JS
$('#content li').each(function (i) {
//for display purpose only
$('#content').append('<span class="list">li(' + i + '):' + $('ul', $(this)).length + '</span>');
//the code you needed
if ($('ul', $(this)).length < 1) {
$(this).on('click', function () {
$('#result').append($(this).parent().html());
});
}
});