jquery tree plugin expanding items - javascript

I have some html that looks like this:
<div class="list-group">
<a id="427" href="#" class="list-group-item"><i class="fa indent0 fa-caret-down"></i>Home</a>
<a id="428" href="#" class="list-group-item"><i class="fa fa-caret-right indent1"></i>Images</a>
<a id="429" href="#" class="list-group-item"><i class="fa fa-caret-right indent1"></i>Videos</a>
<a id="430" href="#" class="list-group-item"><i class="fa indent1 fa-caret-right"></i>Documents</a>
<a id="431" href="#" class="list-group-item" style="display: none;"><i class="fa fa-caret-right indent2"></i>Sub category</a>
<a id="432" href="#" class="list-group-item" style="display: none;"><i class="fa indent2 fa-caret-down"></i>Another sub</a>
<a id="433" href="#" class="list-group-item" style="display: none;"><i class="fa fa-caret-right indent3"></i>Super sub</a>
<a id="434" href="#" class="list-group-item" style="display: none;"><i class="fa fa-caret-right indent2"></i>asdfasdf</a>
<a id="435" href="#" class="list-group-item" style="display: none;"><i class="fa fa-caret-right indent2"></i>asdf</a>
<a id="436" href="#" class="list-group-item"><i class="fa fa-caret-right indent1"></i>Audio</a>
</div>
when I click the .fa I want it to get the indent plus 1 between itself and the next item with the same indent class to unhide.
So in this case,
if we press on id 430, it will show 431, 432, 434 and 435.
Now, that is the easy part. What I need it to also do, is check of any element between 430 and 436 (in this case) and if any of the elements have the class fa-caret-down then it shows the children of that too.
Currently I have made this into a plugin and my code actually works, but I think it could be a lot neater.
I am hoping that some of your gurus will see a simpler solution.
Here is my solution currently:
events: function () {
var self = this;
$(self.element).on("click", ".fa", function (e) {
e.stopPropagation();
var $target = $(this);
var indent = $target.prop('className').match(/\b(indent\d+)\b/)[1];
if ($target.hasClass("fa-caret-right")) {
$target.removeClass("fa-caret-right").addClass("fa-caret-down");
self.showChildren($target.parent(), indent);
if (typeof self.options.onItemExpand === "function") {
self.options.onItemExpand.apply(this, arguments);
}
} else {
var $children = $target.parent().nextUntil("a:has(." + indent + ")");
$target.removeClass("fa-caret-down").addClass("fa-caret-right");
$children.hide();
if (typeof self.options.onItemContract === "function") {
self.options.onItemContract.apply(this, arguments);
}
}
})
},
showChildren: function ($target, className) {
var self = this;
var num = parseInt(className.match(/(\d+)$/)[1], 10);
var $children = $target.nextUntil("a:not(:has(.indent" + (num + 1) + "))");
$.each($children, function (i, item) {
var $item = $(this).find(".fa");
if ($item.hasClass("fa-caret-down")) {
var indent = $item.prop('className').match(/\b(indent\d+)\b/)[1];
self.showChildren($(this), indent);
}
});
$children.show();
}
My solution nearly works (although I hope someone can make it neater) but there is an issue.
If you click Home it will hide all (as expected) but if you click to show again, it will show all but Audio. The same thing happens if you expand down to Documents > Another sub > Super sub and expand that. When you contract it, Audio will vanish.
http://jsfiddle.net/sn92W/
I assume this is to do with nextUntil.
Please help :)

Related

Jquery can't change the classname after one time

Here is my code for Dropdown Menu Appear and Disappear..
If the class name is "dropdown pull-right" then the menu will be closed
If the class name is "dropdown pull-right open" then the menu will be opened
<li class="dropdown pull-right" id="tobechanged">
<a data-toggle="dropdown" class="dropdown-toggle" aria-expanded="false">
<i class="fa fa-user"></i> Admin <b class="caret"></b>
</a>
</li>
So, i wrote this query
$(".dropdown-toggle" ).click(function() {
console.log(document.getElementById("tobechanged").className);
if (document.getElementById("tobechanged").className == "dropdown pull-right" ) {
console.log('opening menu');
document.getElementsByClassName("dropdown-toggle").className = "dropdown pull-right open";
}
else
{
console.log('closing menu');
document.getElementsByClassName("dropdown-toggle").className = "dropdown pull-right";
}
});
It works only in the first time. If i change to another page only the console console.log('opening menu'); works and the class name is changing..
What is the mistake i am doing and how can i make the class name change ?
Note : I am using angularjs, so the page won't be reloaded entirely
I think you should change the Class of the Parent li Element not the a Element inside it.
The issue I see is that once you execute the line:
document.getElementsByClassName("dropdown-toggle").className = "dropdown pull-right open";
This basically will change your html from :
<li class="dropdown pull-right" id="tobechanged">
<a data-toggle="dropdown" class="**dropdown-toggle**" aria-expanded="false">
<i class="fa fa-user"></i> Admin <b class="caret"></b>
</a>
</li>
into
<li class="dropdown pull-right" id="tobechanged">
<a data-toggle="dropdown" class="**dropdown pull-right open**" aria-expanded="false">
<i class="fa fa-user"></i> Admin <b class="caret"></b>
</a>
</li>
Making basically your element no longer available for click event, because the click is subscribed to an element by class name which you change.
$(".dropdown-toggle" )
Hope this is not too confusing.
A fix plus some additional improvements; no need to search for the elements a couple of times
$(".dropdown-toggle" ).click(function() {
var el = document.getElementById("tobechanged");
if (el.className == "dropdown pull-right" ) {
console.log('opening menu');
el.className = "dropdown pull-right open";
}
else {
console.log('closing menu');
el.className = "dropdown pull-right";
}
});
To do it in in JQuery :
$(".dropdown-toggle" ).click(function() {
$(this).closest('#tobechanged').toggleClass('open');
});
Update:
Demo https://jsfiddle.net/n13svnLL/
<pre>you are assigning class name only for dropdown-toggle so it will work only one time for that you have to assign same class name to id (tobechanged) as well</pre>
if (document.getElementById("tobechanged").className == "dropdown pull-right" ) {
console.log('opening menu');
document.getElementsByClassName("dropdown-toggle").className = "dropdown pull-right open";
document.getElementById("tobechanged").className == "dropdown pull-right open";
}
else
{
console.log('closing menu');
document.getElementsByClassName("dropdown-toggle").className = "dropdown pull-right";
document.getElementById("tobechanged").className == "dropdown pull-right"
}

show toggle button on user generated input

I have been trying without success to add a toggle button to a user generated input. this is a font awesome toggle button.
here is my pen: http://codepen.io/lucky500/pen/bdpzbd and the code.
<div id="list" class="greatList clearfix" >
<ul class="greatList" style='display: none;'>
<li class="items">
<div class="box">
<i class="fa fa-toggle-on fa-2x active" id="on"></i>
<i class="fa fa-toggle-on fa-2x fa-rotate-180 inactive" id="off" style='display: none;'></i>
</div>
</li>
</ul>
</div>
Jquery
$(document).ready(function(){
//toggler
$('.box').click(function() {
$('.inactive, .active').toggle();
});
var trash = '<span class="delete">X</span>';
var toggleButton = '<div class="box"></div>';
//To allow the user to use enter
$('#addButton').click(function(e){
e.preventDefault();
var item = $('#addItems').val();
var placeIt = $('<li style="display: block;">' + toggleButton + item + trash + '</li>');
if(!$.trim($('#addItems').val())) {
alert('Please enter text to add to the list');
} else {
$('.greatList').append(placeIt);
};
})
//To remove li when .trash is clicked
$(document).on('click', '.delete', function() {
$(this).closest('li').fadeOut(350);
});
});
all the help is appreciated!
I actually just had to add the i tag with my toggle inside my toggleButton var... now onto trying to get my toggle to work!
var toggleButton = '<div class="box" style="display: block;"><i class="fa fa-toggle-on fa-2x active" id="on"></i></div>';

copying the same thing on same context

i have a problem with jquery i have 2 different set of icons which of each i want to the same thing:
My primary codes are
<ul>
<li>
<a href="">
<i class="fa fa-facebook"></i>
</a>
</li>
<li>
<a href="">
<i class="fa fa-twitter"></i>
</a>
</li>
</ul>
what i want after loading on my browse
<ul>
<li>
<a href="">
<i class="fa fa-facebook"></i>
<i class="fa fa-facebook"></i>
</a>
</li>
<li>
<a href="">
<i class="fa fa-twitter"></i>
<i class="fa fa-twitter"></i>
</a>
</li>
</ul>
i have tried to use
var socials = $("ul a");
socials.each(function ( index,elem ){
var jumpIcons = $( this ).find("i");
socials.append(jumpIcons);
});
but the result is browser is hangged :'(
You need to clone and add the element to the current a
var socials = $("ul a");
socials.each(function (index, elem) {
var jumpIcons = $(this).find("i");
//first you need to clone the current element else, you are just repositioning the current `i` elemnet
//then you need to append it to the current a element, not to all `a` element in the socials - this will cause a lot of iteration if there a lot of `ul a` elements in the page resulting in unresponisve page
$(this).append(jumpIcons.clone());
});
Demo: Fiddle
A simplified version
var socials = $("ul a");
socials.append(function (index, elem) {
return $(this).find("i").clone();
});
Demo: Fiddle
You can make use of clone(), see below code
var socials = $("ul a");
socials.each(function ( index,elem ){
var jumpIcons = $( this ).find("i").clone();//make clone of i
$(this).append(jumpIcons);// use $(this) to get current element
});
Just clone that i for each a with all the events of it(if needed) and append it to a. Try with -
var socials = $("li a");
socials.each(function ( index, elem ){
var jumpIcons = $( this ).find("i").clone(true, true); //remove true, true if you dont need the events
$(this).append(jumpIcons);
});

Select text from div - the not obvious way

Here is my div :
<div id="myDiv">The
<span class="cordial-err-corr" id="good0-0" data-original-title="" title="">best</span>
way to receive congrats
<span>
<span class="cordial-err-corr" id="good0-1" data-original-title="" title="">are</span>
<div class="btn-group">
<a class="btn btn-mini dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a class="prop0-1">is</a></li>
<li><a class="prop0-1">are</a></li>
</ul>
</div>
</span> to give a correct answer.</div>
I would like to get this text : "The best way to receive congrats are to give a correct answer."
So I call $('#myDiv').text(). It does not work since I get the two elements in ul.dropdown-menu.
The next step is to filter or remove the un-wanted children, i.e.:
<div class="btn-group">
<a class="btn btn-mini dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a class="prop0-1">is</a></li>
<li><a class="prop0-1">are</a></li>
</ul>
</div>
I tried to play with $.filter or $.not or $.children with jquery selectors, etc. I cannot find the correct way to do this. Need help!
You can find some code in jsfiddle : http://jsfiddle.net/Ku7FY/
[EDIT]
One solution suggests this code :
$('#result').append( $('#myDiv').text().replace($(".btn-group").text(),"") ).append('<br />')
If several div.btn-group exists, just loop as :
var result = $('#myDiv').text();
$(".btn-group").each(function() {
result = result.replace($(this).text(),"");
});
$('#result').append(result).append('<br />')
So, if I get this straight, you want to exclude the list from the .text(). Try something like:
$('#result').append( $('#myDiv').text().replace($(".dropdown-menu").text(),"") ).append('<br />')
If you don't mind placing all the "dumb" text in spans, then you can do it very simply by only grabbing "dumb" text spans and ".cordial-err-corr" spans (and a find()).
<div class="well" id="myDiv"><span class="cordial-content">The </span>
<span class="cordial-err-corr" id="good0-0" data-original-title="" title="">best </span>
<span class="cordial-content">way to receive congrats </span>
<span>
<span class="cordial-err-corr" id="good0-1" data-original-title="" title="">are </span>
<div class="btn-group">
<a class="btn btn-mini dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a class="prop0-1">is</a></li>
<li><a class="prop0-1">are</a></li>
</ul>
</div>
</span> <span class="cordial-content">to give a correct answer. </span></div>
and
$('#myDiv').find('.cordial-err-corr,.cordial-content').text()
Demo: Fiddle
I have an ugly, though simple enough (I guess) method:
We can make a copy, then remove the dom elements we don't want to display. Then we can get the desired text.
var clone_div = $('#myDiv').clone();
clone_div.find('.btn-group').remove();
$('#result').append(clone_div.text()).append('<br />');
Here is jsfiddle. http://jsfiddle.net/Ku7FY/4/
Try
function process(el){
var array = [];
$(el).contents().each(function(){
var $this = $(this), text;
if(this.nodeType == 3){
text = $.trim($this.text());
if(text){
array.push(text)
}
} else if($this.is(':not(.btn-group)')){
Array.prototype.push.apply(array, process(this))
}
})
return array;
}
console.log(process($('#myDiv')).join(' '))
Demo: Fiddle

inArray() returns false all the time

I'm not sure how to fix it, but I always get -1 when using inArray(). To be more explicit, here's what I'm doing :
<!-- HTML Markup -->
<nav class="navigation clearfix">
<a class="home-anchor" data-class="home-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">Home</span>
</a>
<a class="about-anchor" data-class="about-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">About</span>
</a>
<a class="work-anchor" data-class="work-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">Work</span>
</a>
<a class="shop-anchor" data-class="shop-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">Shop</span>
</a>
<a class="services-anchor" data-class="services-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">Services</span>
</a>
<a class="contact-anchor" data-class="contact-anchor" href="#">
<span class="icons-wrapper">
<i class="icon-normal-state"></i>
<i class="icon-active-state"></i>
</span>
<span class="anchor-text">Contact</span>
</a>
</nav>
/* JavaScript Markup */
var anchors = $(this.cluster_navigation_class).children();
var anchor = (jQuery.inArray(data, anchors) == -1) ? anchors[0] : jQuery.inArray(data, anchors);
Where this.cluster_navigation_class is .navigation, and data is .about-anchor. The JavaScript statement above always returns -1 when I check in console, why is that happening ? Shouldn't it return the index 1 because the class exists at the index 1, or am I wrong ?
You are using inArray wrong. You are comparing a string to a list of elements. jQuery can not magically know what you want to do.
Do you realize you contrdict yourself in your post. One place you are saying checking data and next place you are saying checking class. The data and class are just repeated, so I am not sure what is the problem using either of them.
You should be using find to select the element inside of the navigation element.
var myLink = jQuery(".navigation").find('[data-class="about-anchor"]');
Another way to use filter:
var links = jQuery(".navigation a");
var activeElem = links.filter( function(){ return jQuery(this).data()==="about-anchor"; } );
var myLink = activeElem.length===1 ? activeElem : links.eq(0);
or with the class
var links = jQuery(".navigation a");
var activeElem = links.filter(".about-anchor");
var myLink = activeElem.length===1 ? activeElem : links.eq(0);

Categories