I have a foreach loop here. For every item it prints, there is a comment section that is expandable/collapsable.
The problem I have is, when I hit the "Expand All" for an item, it expands the comments for EVERY item in the loop all at once. Whereas, I only want to see the comments for that specific item.
I know it has something to do with IDs and Classes, but I have zero experience with JQuery. So please help!
<?php
foreach ($items as $item) {
echo item['comment'];
echo $item['full_name']; ?>
<div id="listContainer">
<div class="listControl">
<a class="expandList">Expand All</a>
<a class="collapseList">Collapse All</a>
</div>
<ul class="expList">
<li>Item A
<ul>
<li>Item A.1
<ul>
<li><span>fidjis</span></li>
</ul>
</li>
<li>Item A.2</li>
<li>Item A.3
<ul>
<li><span>iejowejfiojwiefj.</span></li>
</ul>
</li>
</ul>
</li>
<li>Item B</li>
<li>Item C
<ul>
<li>Item C.1</li>
<li>Item C.2
<ul>
<li><span>sdfkjksdjfnjkdsfnjn</span></li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<?php
}
?>
and here is the Jquery:
function prepareList() {
$('.expList').find('li:has(ul)')
.click( function(event) {
if (this == event.target) {
$(this).toggleClass('expanded');
$(this).children('ul').toggle('medium');
}
return false;
})
.addClass('collapsed')
.children('ul').hide();
//Create the button functionality
$('.expandList')
.unbind('click')
.click( function() {
$('.collapsed').addClass('expanded');
$('.collapsed').children().show('medium');
})
$('.collapseList')
.unbind('click')
.click( function() {
$('.collapsed').removeClass('expanded');
$('.collapsed').children().hide('medium');
})
};
You are creating the DIV with ID "listContainer" on every iteration of your loop, this is invalid. An ID should only be used once.
Would suggest changing the Id="listContainer" to class="listContainer"
For the jQuery, the problem is that you are referencing all elements with the 'collapsed' class, rather than just the ones that are within the container relating to the Expand All button.
I haven't tested this, but you would want something along these lines
//Create the button functionality
$('.expandList')
.unbind('click')
.click( function() {
$(this).parents('.listContainer').find('.collapsed').addClass('expanded');
$(this).parents('.listContainer').find('.collapsed').children().show('medium');
})
$('.collapseList')
.unbind('click')
.click( function() {
$(this).parents('.listContainer').find('.collapsed').removeClass('expanded');
$(this).parents('.listContainer').find('.collapsed').children().hide('medium');
})
The difference here is that you start from the button clicked and div with the 'listContainer' class, then work down from there to find all the '.collapsed' elements.
First you should change listContainer into a class attribute.
as ID´s must be unique.
function prepareList() {
$('.expList').find('li:has(ul)')
.click( function(event) {
if (this == event.target) {
$(this).toggleClass('expanded');
$(this).children('ul').toggle('medium');
}
return false;
})
.addClass('collapsed')
.children('ul').hide();
//Create the button functionality
$('.expandList')
.unbind('click')
.click( function() {
// walk up the tree from the clicked button
// and find the parent .listContainer
var $list = $(this).parents(".listContainer");
// find .collapsed in .listContainer
$list.find('.collapsed').addClass('expanded');
$list.find('.collapsed').children().show('medium');
});
$('.collapseList')
.unbind('click')
.click( function() {
// walk up the tree from the clicked button
// and find the parent .listContainer
var $list = $(this).parents(".listContainer");
// find .collapsed in .listContainer
$list.find('.collapsed').removeClass('expanded');
$list.find('.collapsed').children().hide('medium');
});
};
I think you are using classes in place for ID. IDs are used to identify a single elements whereas a class can be applied to more that 1 element. Try to use IDs in your jQuery calls when accessing an individual element. You can try something like:
?>
<div id="listContainer">
<div class="listControl">
<a class="expandList">Expand All</a>
<a class="collapseList">Collapse All</a>
</div>
<ul id="list1" class="expList">
<li>
Item A
<ul>
<li>
Item A.1
<ul>
<li>
<span>fidjisjfisdjfisdjifjsidfj.</span>
</li>
</ul>
</li>
<li>
Item A.2
</li>
<li>
Item A.3
<ul>
<li>
<span>iejowejfiojwiefj.</span>
</li>
</ul>
</li>
</ul>
</li>
<li>
Item B
</li>
<li>
Item C
<ul>
<li>
Item C.1
</li>
<li>
Item C.2
<ul>
<li>
<span> sdfkjksdjfnjkdsfnjn. </span>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
<?php
}
?>
and your jQuery will be something like
function prepareList() {
$('#list1').find('li:has(ul)')
.click( function(event) {
if (this == event.target) {
$(this).toggleClass('expanded');
$(this).children('ul').toggle('medium');
}
return false;
})
.addClass('collapsed')
.children('ul').hide();
//Create the button functionality
$('.expandList')
.unbind('click')
.click( function() {
$('.collapsed').addClass('expanded');
$('.collapsed').children().show('medium');
})
$('.collapseList')
.unbind('click')
.click( function() {
$('.collapsed').removeClass('expanded');
$('.collapsed').children().hide('medium');
})
};
Related
I want to insert the (ul) tag that comes after the (div) tag in class (copy1).
Then, by clicking on the tag (div) in the class (copy1), insert the (ul) tag after the tag (div) into the class (copy2). The first step is running the code, but I don't know the second step.
$(document).ready(function() {
$('ul ul').hide();
$('ul div').click(function() {
var x = $(this).next().html();
$('.copy1').html(x);
})
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li>
<div>01</div>
<ul>
<li>01-01</li>
<li>
<div>01-02</div>
<ul>
<li>01-02-01</li>
<li>01-02-02</li>
<li>01-02-03</li>
</ul>
</li>
<li>01-03</li>
</ul>
</li>
<li>
<div>02</div>
<ul>
<li>02-01</li>
<li>02-02</li>
<li>02-03</li>
</ul>
</li>
</ul>
<hr>
<ul class="copy1"></ul>
<ul class="copy2"></ul>
After clicking on the first div tag, the following values are inserted into the copy1 class.
01-01
01-02
01-03
But by clicking on 01-02 the following values
01-02-01
01-02-02
01-02-03
Those that are in the copy1 class are not copied to the Copy2 class.
Because the elements in .copy1 are created dynamically, you either need to add events after they are created or use event delegation
$(document).on("click", ".copy1 div", function() { ...
as you want copy1->copy2, it needs to be separate from the src->copy1 code (or have additional logic within the click handler).
In the snippet below, I've kept them separate for clarity. I've also added some css to show which ones can be clicked as it was slightly confusing that 01-01 does nothing as there are no child nodes.
$(document).ready(function() {
$('ul ul').hide();
$('ul div').click(function() {
var x = $(this).next().html();
$('.copy1').html(x);
})
$(document).on("click", ".copy1 div", function() {
var x = $(this).next().html();
$('.copy2').html(x);
});
});
ul div { color: red }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li>
<div>01</div>
<ul>
<li>01-01</li>
<li>
<div>01-02</div>
<ul>
<li>01-02-01</li>
<li>01-02-02</li>
<li>01-02-03</li>
</ul>
</li>
<li>01-03</li>
</ul>
</li>
<li>
<div>02</div>
<ul>
<li>02-01</li>
<li>02-02</li>
<li>02-03</li>
</ul>
</li>
</ul>
<hr>
<ul class="copy1"></ul>
<ul class="copy2"></ul>
I have a list of <li> tags inside a <ul> , I have a javascript function that add 'active' class to one of them when clicked and remove it from its siblings , I want to implement this to all except the last <li>
<ul class='tabs'>
<li>Home</li>
<li>About</li>
<li>Contact</li>
<li>
<form>
<input type='text'/>
<button></button>
</form>
</li>
</ul>
The JS function:
[].forEach.call(document.querySelectorAll('.tabs li'), function(ele) {
ele.addEventListener('click', function(e) {
if(ele.nextSibling){
document.querySelector(".tabs li.active").classList.remove("active");
ele.classList.add("active");
}
});
});
You can use the slice method.
const listItems = Array.from(document.querySelectorAll('.tabs li'));
const selectableListItems = listItems.slice(0, -1);
selectableListItems.forEach(function(ele) {
ele.addEventListener('click', function(e) {
if(ele.nextSibling){
document.querySelector(".tabs li.active").classList.remove("active");
ele.classList.add("active");
}
});
});
Or adjust your query selector using a pseudo-class in order to select all items except the last one:
document.querySelectorAll('.tabs li:not(:last-child)');
I have elements which have nested li elements and i made a click function to get the value. Every time i click i am getting the same value again and again.
<script type="text/javascript">
$('.cat-select').on('click',function(){
$('.cat-list').css('display','block');
$('.sub-list').css('display','block');
});
$(document).on('click','.cat-list>li',function(){
var selectedVal = $(this).clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text(); //get the text of elemen
console.log(selectedVal);
$('.cat-select').text(selectedVal);
});
</script>
<div class="form-group">
<label for="input-placeholder" class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<style type="text/css">
.cat-list, .sub-list{ display: none; }
</style>
<div class="cat-group">
<button class="cat-select" type="button">Select Category</button>
<ul class="cat-list">
<li class="have-child">Electronics
<ul class="sub-list">
<li class="have-child"> Mobiles & Tablets
<ul class="sub-list">
<li>Mobiles</li>
<li>Tablets</li>
<li class="have-child">Accessories</li>
<ul>
<li>Power Bank</li>
<li>Phone Cases</li>
</ul>
</ul>
</li>
<li class="have-child">Cameras
<ul class="sub-list">
<li>DSLRs</li>
<li>Drones</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
Every time i click i am getting the same value
https://jsfiddle.net/yx4Ldt80/
The issue you see is because you only attach the event handler to the child of the .cat-list through your use of the > operator.
To solve this, remove that from the selector and call stopPropagation() on the event to stop it bubbling up the DOM. Try this:
$(document).on('click', '.cat-list li', function(e) {
e.stopPropagation();
var selectedVal = $(this).clone().children().remove().end().text();
$('.cat-select').text(selectedVal);
})
$('.cat-select').on('click', function() {
$('.cat-list, .sub-list').toggle();
});
$(document).on('click', '.cat-list li', function(e) {
e.stopPropagation();
var selectedVal = $(this).clone().children().remove().end().text();
$('.cat-select').text(selectedVal);
})
.cat-list,
.sub-list {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div class="form-group">
<label for="input-placeholder" class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div class="cat-group">
<button class="cat-select" type="button">Select Category</button>
<ul class="cat-list">
<li class="have-child">Electronics
<ul class="sub-list">
<li class="have-child">
Mobiles & Tablets
<ul class="sub-list">
<li>Mobiles</li>
<li>Tablets</li>
<li class="have-child">Accessories
<ul>
<li>Power Bank</li>
<li>Phone Cases</li>
</ul>
</li>
</ul>
</li>
<li class="have-child">
Cameras
<ul class="sub-list">
<li>DSLRs</li>
<li>Drones</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
Also note that I fixed the HTML in your 'Accessories' ul as it was outside of the parent li.
"Never use" Event.stopPropagation()
The worst thing you can do (as suggested by answers here and around the web) is to use Event.stopPropagation().
Don't use Event.stopPropagation(), well, unless you really know what you're doing.
An Application should be always aware of every event happening in it's context.
Imagine you build a popup, modal, or a custom select-box that needs to close if you click anywhere else in the page. Well, congratulations, a LI element just stopped you from doing so.
Use Event.target instead
function myClickHandler(ev) {
if (ev.target !== this) return; // Ignore if non-this called the event
console.log( this.textContent );
}
Here's an example with your specific use-case:
const $cat = $('.cat-select');
$cat.on('click', function() {
$('.cat-list, .sub-list').toggle();
});
$(document).on('click', '.cat-list li', function(ev) {
if (ev.target !== this) return; // Ignore if non-this called the event
const value = $(this).contents().filter((i, el) => el.nodeType == 3)[0].nodeValue;
$cat.text(value);
});
// Than, 3000 lines later... THANK YOU BECAUSE:
$('body').on('click', function() {
// Close a popup or something
console.clear(); console.log(`YEY I registered an event!
Thank you for not using Event.preventDefault()`);
});
.cat-list,
.sub-list {
display: none;
}
<div class="form-group">
<label for="input-placeholder" class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div class="cat-group">
<button class="cat-select" type="button">Select Category</button>
<ul class="cat-list">
<li class="have-child">Electronics
<ul class="sub-list">
<li class="have-child"> Mobiles & Tablets
<ul class="sub-list">
<li>Mobiles</li>
<li>Tablets</li>
<li class="have-child">Accessories</li>
<ul>
<li>Power Bank</li>
<li>Phone Cases</li>
</ul>
</ul>
</li>
<li class="have-child">Cameras
<ul class="sub-list">
<li>DSLRs</li>
<li>Drones</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Get the clicked element based the event.target property. Although you can avoid the clone by contents() and filter() methods which help to filter out text nodes.
$(document).on('click', 'li', function(e) {
// if target element is not `li` tag then get closest li tag
var selectedVal = (e.target.nodeType == 'LI' ? $(e.target) : $(e.target).closest('li'))
.contents() // get all children nodes
.filter(function() { // filter out text nodes
return this.nodeType === 3;
}).text(); // get text content
$('.cat-select').text(selectedVal);
})
$(document).on('click', 'li', function(e) {
// if target element is not `li` tag then get closest li tag
var selectedVal = (e.target.nodeType == 'LI' ? $(e.target) : $(e.target).closest('li'))
.contents() // get all children nodes
.filter(function() { // filter out text nodes
return this.nodeType === 3;
}).text(); // get text content
$('.cat-select').text(selectedVal);
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="form-group">
<label for="input-placeholder" class="col-sm-2 control-label"></label>
<div class="col-sm-10">
<div class="cat-group">
<button class="cat-select" type="button">
Select Category</button>
<ul class="cat-list">
<li class="have-child">Electronics
<ul class="sub-list">
<li class="have-child">Mobiles & Tablets
<ul class="sub-list">
<li>Mobiles</li>
<li>Tablets</li>
<li class="have-child">Accessories</li>
<ul>
<li>Power Bank</li>
<li>Phone Cases</li>
</ul>
</ul>
</li>
<li class="have-child">Cameras
<ul class="sub-list">
<li>DSLRs</li>
<li>Drones</li>
</ul>
</li>
</ul>
</li>
</ul>
</div>
</div>
i hope this one is helping u
$('.cat-select').each(function() {
$(this).on('click', function() {
$('.cat-list').css('display', 'block');
$('.sub-list').css('display', 'block');
});
});
$(document).on('click', function() {
$(this).each('.cat-list li', function() {
var selectedVal = $(this).clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text(); //get the text of elemen
console.log(selectedVal);
$('.cat-select').text(selectedVal);
});
});
I have toggle tree which responds on click i.e. show or hide UL>li. I want to hide all the UL --> li who has parent ul-li. To make it more obvious I have apply css background-color to red which I want to be hidden by when page loads but show when it click back.
https://jsfiddle.net/toxic_kz/vr84pd6u/
<div>
<ul class="treeview">
<li><a>System Administration</a></li>
<li>
<a>System Core</a>
<ul>
<li><a>f2</a></li>
<li>
<a>f3</a>
<ul>
<li><a>f4</a></li>
<li><a>f5</a></li>
<li><a>f6</a></li>
</ul>
</li>
<li>
<a>f7</a>
<ul>
<li>
<a>f8</a>
<ul>
<li>
<a>f10</a>
<ul>
<li><a>f11</a></li>
</ul>
</li>
</ul>
</li>
<li><a>f9</a></li>
</ul>
</li>
</ul>
</li>
<li>
<a>MyFunctionA</a>
<ul>
<li>
<a>f12</a>
<ul>
<li><a>f13</a></li>
<li><a>f14</a></li>
</ul>
</li>
<li><a>f16</a></li>
</ul>
</li>
<li><a>Course Management</a></li>
</ul>
</div>
jQuery
<script type="text/javascript">
$(document).ready(function () {
$('.treeview li').each(function () {
if ($(this).children('ul').length > 0) {
$(this).addClass('parent');
}
});
// $('.treeview li.parent>ul li').css('display', 'none');
$('.treeview li.parent>ul li').css('background-color', 'red');
$('.treeview li.parent > a').click(function () {
$(this).parent().toggleClass('active');
$(this).parent().children('ul').slideToggle('fast');
});
});
</script>
If I understood your question correctly, this is what you want:
$(".treeview").find('ul').hide()
Place this in your $(document).ready function and it'll hide the underlying unordered list upon page load.
Ok guys, as promised, here is the real deal, first the sample html:
<li data-foo="bar">
<span id="a"></span>
<ul>
<li>
<span id="1"></span>
<ul>
<li>
<span id="b"></span>
</li>
</ul>
</li>
<li>
<span id="2"></span>
<ul>
<li>
<span id="c"></span>
</li>
</ul>
</li>
</ul>
</li>
<li data-foo="bar">
<span id="d"></span>
<ul>
<li>
<span id="3"></span>
<ul>
<li>
<span id="e"></span>
</li>
</ul>
</li>
<li>
<span id="4"></span>
<ul>
<li>
<span id="f"></span>
</li>
</ul>
</li>
</ul>
</li>
I want to get the window to pop up "12", then pop up "34"... so here is my nested functions attempt:
<script>
var poptext = "";
$('li[data-foo=bar]').each(
function () {
$(this li span).each(function () {
poptext = poptext + $(this).attr("id");
}
alert(poptext);
poptext = "";
);
}
);
</script>
This does not seem to be working, I think Jquery might got confused with multiple "this" keywords? Also there could be something wrong with the selector for those spans to begin with.
Thanks guys!
I think you are looking for the direct descendant selector: >
Used like this:
$('li[data-foo=bar]').each(function () {
var poptext = "";
$(this).find('> ul > li > span').each(function () {
poptext = poptext + $(this).attr("id");
});
alert(poptext);
});
Will only select the nodes that are direct children of the previous expression on the left hand side.
A more verbose, but maybe readable version could be:
$(this).children('ul').children('li').children('span') // selects the same
See it in action here: http://jsfiddle.net/xYgJg/
You have syntax errors in your code, $(this li span) should be $('li span', this) also your logic is off, you aren't filtering out the spans with the letter ids.