Finding all parents and checking them in a treeview? - javascript

I am working with this code on this page: http://experiments.wemakesites.net/css3-treeview-with-multiple-node-selection.html
My problem, is that it want the selection to go in reverse. That is, to check all the parents of the box checked instead of the children.
For example, in the link above, if you were to click My Documents, I would want Documents and Libraries checked as well.
I have tried using parents(), and multiple versions of parent().parent().prev().prev(), and the only thing I came up with that was somewhat functional was this:
$(".acidjs-css3-treeview").delegate("label input:checkbox", "change", function() {
var
checkbox = $(this),
nestedList = checkbox.parent().parent().parent().prev().prev(),
selectNestedListCheckbox = nestedList.find("input:checkbox");
if(checkbox.is(":checked")) {
return selectNestedListCheckbox.prop("checked", true);
}
selectNestedListCheckbox.prop("checked", false);
});
This would pick only the first parent, and I would need all the parents. I suppose I could construct a loop to keep using this same script, but it seems very ugly.
What would be a cleaner way of doing what I am looking for?

You can do this using jQuery's parents() method, then checking the immediate children for inputs. You might need to modify this depending on the layout of your html. I've demonstrated this with a jsfiddle.
The key part is:
var checkboxes = $this.parents().children('input');
In full:
$('input').on('change', function() {
var $this = $(this);
var checkboxes = $this.parents().children('input');
if($this.is(":checked")) {
return checkboxes.prop("checked", true);
}
checkboxes.prop("checked", false);
});

Related

Using jQuery to change the order of Select Options not working correctly

I have a friend that helped me code this as I am unable to get it working correctly. I am trying to edit the order of the Select list items as I am unable to edit the list manually because the list is populated within a Wordpress plugin called SellMedia. The code supplied currently works but when you click on the dropdown list it works, then if you click off and back on it change the order.
Is there a way to stop this from happening?
$(document).on('click','#sell_media_item_size',function(){
$("#sell_media_item_size option").slice(1, 19).each(function () {
var options = $('#sell_media_item_size option');
$(this).insertAfter($(options[0]));
});
});
Example here - https://jsfiddle.net/7bbxd42b/5/
You should order only once. You can use a flag on the global level to check that you have already ordered the entries:
var ordered = false;
$(document).on('click','#sell_media_item_size',function(){
if (!ordered) {
$("#sell_media_item_size option").slice(1, 19).each(function () {
var options = $('#sell_media_item_size option');
$(this).insertAfter($(options[0]));
});
ordered = true;
}
});
It would be even better, if you could check it some other way, so you would not need the global flag.
Update: I just found out, you can use .one() instead of .on() to bind the click event. So the handler will be fired only once.

Chaining jQuery .wrap() with .after()

Ok, so I am writing a small plugin...nothing big, but I ran across this issue (stripped down to show isolated problem)
My Plugin uses jQuery .wrap() and in this example I'll be using .after() as well.
(function( $ ) {
var newWrap = $('<div/>');
$.fn.smartform = function() {
this.wrap(newWrap);
return this;
};
}( jQuery ));
Implementation mixed with .after()
$(document).on('click', '#addInput', function(){
var newInput = $('<input/>').smartform().val('Just Added');
$(this).after(newInput);
});
My function returns the correct targeted object properly since I see the value of the new input is 'Just Added' but the input does not get wrapped and I don't understand why.
I can get it to work this way (below) but it is not ideal as depending on how the new element is inserted ( .append .before .after ) will force traversing differently to target the newly added element.
$(document).on('click', '#addInput', function(){
var newInput = $('<input/>').val('Just Added');
$(this).after(newInput).next('input').smartform();
});
Any thoughts would be great, If you have any questions fell free to ask...
Thank you in advance
The problem is newInput is referring to the input element you have created, which is still not part of the dom. When you wrap the input the wrapper is created only in memory not in the dom.
So when you use newInput in after(), the input is removed form the in memory div and is inserted after the button so the wrapper element is lost.
$(document).on('click', '#addInput', function () {
var newInput = $('<input/>').smartform().val('Just Added');
$(this).after(newInput.parent());
});
Demo: Fiddle

Show only one element and hide others without toggle

I've been researching this and many of the answers involve toggle function. The problem is I want one elements to be shown at all time, which isn't possible if toggle is used (they may accidently click something and it disappears). So I was doing it this way:
$(document).ready(function(){
$("#goals").hide();
$("#History").addClass("selected");
$("#History").click(function(e){
e.preventDefault();
$("#history").show();
$("#goals").hide();
$("#History").addClass("selected");
$("#Goals").removeClass("selected");
});
$("#Goals").click(function(e){
e.preventDefault();
$("#goals").show();
$("#history").hide();
$("#Goals").addClass("selected");
$("#History").removeClass("selected");
});
});
Except it's prob too tedious and I'm sure there's a better way. I'm trying to find solutions that use hide and show only or if the requirement can be fulfilled. Any help is appreciated...I'm not advanced at jQuery yet so please provide explanation. Thank you
You can give them a class.. or just combine the them in one selector
$(document).ready(function(){
$("#goals").hide();
$("#History").addClass("selected");
var eles = $("#History,#Goals");
eles.click(function(e){
e.preventDefault();
$(this).show().addClass('selected'); // show and add class to current clicked
eles.not(this).hide().removeClass('selected'); // hide and remove class for the other one
});
});
EDIT: I didn't notice you actually had different id's
var hg = $("#history,#goals");
var HG = $("#History,#Goals");
HG.click(function(e){
e.preventDefault();
var el = $('#'+this.id.toLowerCase());
el.show();
hg.not(el).hide();
$(this).addClass("selected");
HG.not(this).removeClass("selected");
});

How to use jQuery after new objects are injected into DOM?

I am making a Sentence Generator. So far, it can take a list of words. Then it gets data from sentence.yourdictionary.com to take a sentence. I display the first sentence from sentence.yourdictionary.com using $("ul.example>li").first().Then it is put into a paragraph <p id="sents">.
So if you entered in the words yo and nose your output would be
<p id="sents"><li id="yo"><strong>Yo</strong> ' money
back a hund'd times, de preacher says!</li><li id="nose">I will no longer be caught with a
bleeding <strong>nose</strong>, heart palpatations, week long benders or hurting myself in any major way.</li></p>
I want a function to be called when you hover over the new list items.
$(document).ready( function() {
$("li").hover( function () {
$(this).append("<span>Testing</span>");
var id = $(this).attr("id");
console.log(id);
}, function () {
$(this).find("span:last").remove();
});
});
This doesnt work after the new list items are injected into the DOM. I tried adding an event listener for mousemove, but then when you hover over it the word "test" shows up a bunch of times! How can I make it happen after the new list items are injected?
Here is a jfiddle if you want some clarification: http://jsfiddle.net/varhawk5/cNKyx/1/
Thank you so much. Sorry I'm just learning javascript!
EDIT
To fix this issue, I used the .on() function as the comments suggested. There is no "hover" event though, so I think this is the only way.
$("body").on("mouseenter", "li#topWord", function() {
var word = $(this).data("word");
var sents = sentences[word]
$(this).html("<div class='restOfSents' data-word='" + word +"'></div>");
for(var i=1; i<sentences[word].length; i++) {
$(".restOfSents").append($(sentences[word][i]));
}
console.log(sents);
});
$("body").on("mouseleave", "li", function() {
// Remove the new div
});
$(document).ready( function() {
$(document).on('hover','li', function () {
$(this).append("<span>Testing</span>");
var id = $(this).attr("id");
console.log(id);
}, function () {
$(this).find("span:last").remove();
});
});
You're right! The reason for this, is that $(document).ready() only gets called on page load. You can either manually add a new event hook to each new element as you add it, or take advantage of jQuery's "on" functionality which will automatically detect new dom elements which match your criteria.
You should make use of .on() rather than .hover():
$('li').on('mouseenter', function() { ... })
Also you shouldn't use IDs for this. Make use of data-* attributes instead. Otherwise your code will break when a user enters the same word twice (as IDs are unique).
var id = $(this).attr('data-example');

Do looping through items and adding handlers to it hurt performance

Does it hurt in performance when I loop through list-items and add a click-handler to all separate items?
The reason I do this is because I would only like to make the list item clickable if it contains an hyperlink.
The code I'm currently using is:
$('ul.paginator li').each(function() {
if ($('a', this).length > 0) {
$(this).css('cursor', 'pointer');
$(this).click(function() {
location.href = $('a', this).attr('href');
});
}
});
I'm not sure how much it might hurt performance, but have you considered using a somewhat simplified jQuery selector:
$('ul.paginator li:has(a)').each(
function(){
$(this).css('cursor','pointer').click(
function(){
location.href = $(this).find('a').attr('href');
});
});
Incidentally, the performance would depend on the number of elements you're searching through more than anything else. Just a few and it's likely to be imperceptible, a few thousand and it will (probably) be noticeable.
Edited to reduce the expense of has():
$('ul.paginator li a').each(
function(){
var address = this.href;
$(this).closest('li').css('cursor','pointer').click(
function(){
location.href = address;
});
});
This should be less expensive, as it will select only those a elements within an li, and then move up to affect that li element.
depends how many rows there are. If there are thousands of them, then yes. If there are a modest amount then not really enough to be noticeable.
An alternative approach would be to put the click handler on the element that contains the items, and then when a click event comes in, to use the data in the event passed to the handler to determine what to do. One handler.
Yes, it is better to use delegate with a proper selector that selects only the items you want.
There will be only one handler created and attached.
If you don't want to use has() than this will be enough (no need for multiple handlers):
$('ul.paginator').delegate('click', 'li', function() {
var link = $('a', this);
if (link.length > 0) {
location.href = link.attr('href');
}
});

Categories

Resources