jQuery toggle select from select option mechanism - javascript

I am trying to toggle via a select option. I am having difficulties toggling more than two. My goal is to be able toggle as far as 4 through the select option. For example Categories and subcategories. Here is my example in jsfiddle.
<script type="text/javascript">
var op = $("#tables option[value='options']:selected");
var os = $("#tables option[value='Example2']:selected");
if (op.length)
$("#something").show();
else
$("#something").hide();
if (op == ("#something").show())
$("#something2").show();
else
$("#something2").hide();
}​
</script>

Is this the design pattern you are looking for?
if your object is not selected
if your parent is selected
you are also selected
else
you are selected
else
you are now unselected
This logic will work for any depth of recursion.
EDIT: Assuming each menu is a ul. You'll have to tweak the selectors.
This is just one way to do it. Not the best if you have events firing on visibility, or if you have ui reflow issues.
clickyclicky = function(event) {
var $target = $(event.currentTarget);
if (!$target.hasClass("selected")) {
// hide the old target and its parents
var $oldTarget = $('.selected');
$oldTarget.removeClass("selected").hide().parents('ul').hide();
// show the new target and its parents
$target.show().addClass("selected").parents('ul').show();
} else {
// hide the target
$target.removeClass("selected").hide();
// move the selected token to the parent.
$parent = $target.parent().parent(); // assuming an ul/li tree pattern.
if ($parent.is('ul')) {
$parent.addClass("selected");
}
}
}
I haven't tested this code, it's just a general reference.
EDIT: Here's the Fiddle: http://jsfiddle.net/uA7XD/76/

Related

jQuery hiding child element opens its child

Im using a table and rows can have child rows and it can go down a few levels,
what is happening now is that when hiding a child element it then opens that childs child element.
Heres my jQuery:
$(document).ready(function() {
function getChildren($row) {
var children = [], level = $row.attr('data-level');
while($row.next().attr('data-level') > level) {
children.push($row.next());
$row = $row.next();
}
return children;
}
$('.parent').on('click', function() {
var children = getChildren($(this));
$.each(children, function() {
$(this).toggle();
})
});
$(".parent a").click(function(e) {
e.stopPropagation();
})
})
I have set up a jsfiddle so you can see whats happening
https://jsfiddle.net/rhvye8k0/4/
If you click the first "+" you will see what im trying to describe.
Cant think how to sort it out
Update,
have sorted it and updated jsfiddle https://jsfiddle.net/rhvye8k0/5/
There may be a way to reduce the jQuery but it works for now
Your problem is the $(this).toggle(); in .parent's onclick handler. The tr at level 3 has style="display:none", the others don't. toggle() will toggle the receiving element(s) visibility so the others are show (their display is implicitly block) and level 3's is hidden.

filtering only previously unselected <select> options qith JQuery

Previously I asked how to do this and was directed to this:
<script>
jQuery.fn.filterByText = function(textbox) {
return this.each(function() {
var select = this;
var options = [];
$(select).find('option').each(function() {
options.push({value: $(this).val(), text: $(this).text()});
});
$(select).data('options', options);
$(textbox).bind('change keyup', function() {
var options = $(select).empty().scrollTop(0).data('options');
var search = $.trim($(this).val());
var regex = new RegExp(search,"gi");
$.each(options, function(i) {
var option = options[i];
if(option.text.match(regex) !== null) {
$(select).append(
$('<option>').text(option.text).val(option.value)
);
}
});
});
});
};
</script>
(http://www.lessanvaezi.com/filter-select-list-options/)
When I use this filter on the select box it filters both the unselected AND the selected. I'd like it to ONLY filter the unselected because if a user wants to ammend the selections and filters again, the previously selected items go away - unless they meet the filter criteria.
I'm not that good at JavaScript or JQuery and can't understand how I might tell the above script to ignore options that are ":selected" but filter all else.
Here's a jfiddle if it helps: http://jsfiddle.net/UmKXy/ I'd like option one and two to remain selected and in the list when user begins to type.
Thanks for help!
The solution you had would not work with selected elements because he created an array of options at the start and then matched those options against the regex(Without regards to what is actually selected). I've used spans to hide options in the past and created an example for you to see how it works. Here is the link : http://jsfiddle.net/rD6wv/
Here is the code
$(function() {
$("#filterByText").bind('keyup',function(){
var search = $.trim($(this).val());
var regex = new RegExp(search,"gi");
$("#filez").find('option').each(function(){
if(!$(this).is(':selected')){
if($(this).val().match(regex) === null) {
$(this).wrap('<span>');
}else if($(this).parent().is('span')){
$(this).parent().replaceWith($(this));
}
}
});
});
});
You simply need to loop through all the options of the select when you type in the textbox.
You then check if it is selected, if it is you do nothing, else you check if it matches the search filter, if it does you wrap it in a span, making it invisible, else it means you need to see it, so you check if it is already wrapped in a span, and in that case you replace it with the option so you can see it again.
to selected the non selected options, use this:
$('option:not[selected]') or $('#myselect > option:not[selected]')
to remove them, use this:
$('option:not[selected]').remove();
in css, :not filters for opposite of what comes in the curved brackets.
and [] is attribute selector.
so :not[selected] means: does not have an attribute whose key is "selected"

Checking if Element hasClass then prepend and Element

What I am trying to achieve here is when a user clicks an element it becomes hidden, once this happens I want to prepend inside the containing element another Element to make all these items visible again.
var checkIfleft = $('#left .module'),checkIfright = $('#right .module');
if(checkIfleft.hasClass('hidden')) {
$('#left').prepend('<span class="resetLeft">Reset Left</span>');
} else if(checkIfright.hasClass('hidden')) {
right.prepend('<span class="resetRight">Reset Right</span>');
}
I tried multiple ways, and honestly I believe .length ==1 would be my best bet, because I only want one element to be prepended. I believe the above JS I have will prepend a new element each time a new item is hidden if it worked.
Other Try:
var checkIfleft = $('#left .module').hasClass('hidden'),
checkIfright = $('#right .module').hasClass('hidden');
if(checkIfleft.length== 1) {
$('#left').prepend('<span class="resetLeft">Reset Left</span>');
} else if(checkIfright.length== 1) {
right.prepend('<span class="resetRight">Reset Right</span>');
}
else if(checkIfleft.length==0){
$('.resetLeft').remove()
} else if (checkIfright.length==0){
$('.resetRight').remove()
}
Basically if one element inside the container is hidden I want a reset button to appear, if not remove that reset button...
hasClass() only works on the first item in the collection so it isn't doing what you want. It won't tell you if any item has that class.
You can do something like this instead where you count how many hidden items there are and if there are 1 or more and there isn't already a reset button, then you add the reset button. If there are no hidden items and there is a reset button, you remove it:
function checkResetButtons() {
var resetLeft = $('#left .resetLeft').length === 0;
var resetRight = $('#left .resetRight').length === 0;
var leftHidden = $('#left .module .hidden').length !== 0;
var rightHidden = $('#right .module .hidden').length !== 0;
if (leftHidden && !resetLeft) {
// make sure a button is added if needed and not already present
$('#left').prepend('<span class="resetLeft">Reset Left</span>');
} else if (!leftHidden) {
// make sure button is removed if no hidden items
// if no button exists, this just does nothing
$('#left .resetLeft').remove();
}
if (rightHidden && !resetRight) {
$('#right').prepend('<span class="resetRight">Reset Right</span>');
} else if (!rightHidden) {
$('#right .resetRight').remove();
}
}
// event handlers for the reset buttons
// uses delegated event handling so it will work even though the reset buttons
// are deleted and recreated
$("#left").on("click", ".resetLeft", function() {
$("#left .hidden").removeClass("hidden");
$("#left .resetLeft").remove();
});
$("#right").on("click", ".resetRight", function() {
$("#right .hidden").removeClass("hidden");
$("#right .resetRight").remove();
});
FYI, if we could change the HTML to use more common classes, the separate code for left and right could be combined into one piece of common code.
Add the reset button when hiding the .module, if it's not already there :
$('#left .module').on('click', function() {
$(this).addClass('hidden');
var parent = $(this).closest('#left');
if ( ! parent.find('.resetLeft') ) {
var res = $('<span />', {'class': 'resetLeft', text : 'Reset Left'});
parent.append(res);
res.one('click', function() {
$(this).closest('#left').find('.module').show();
$(this).remove();
});
}
});
repeat for right side !
I've recently experimented with using CSS to do some of this stuff and I feel that it works quite well if you're not trying to animate it. Here is a jsfiddle where I can hide a module and show the reset button in one go by adding/removing a 'hideLeft' or 'hideRight' class to the common parent of the two modules.
It works by hiding both reset button divs at first. Then it uses .hideLeft #left { display:none;} and .hideLeft #right .resetLeft { display: block; } to hide the left module and display the reset button when .hideLeft has been added to whichever element both elements descend from. I was inspired by modernizr a while back and thought it was a neat alternative way to do things. Let me know what you think, if you find it helpful, and if you have any questions :)

Collapsible list with jQuery - How to update Expand/Collapse all button

I've got a list of items which can be expanded/collapsed individually or all at once with an Expand All/Collapse All button.
All the items start collapsed, but if you manually expand item so that every item is expanded, the 'Expand All' button should change to 'Collapse All'. Similarly if you collapse all the items it should change to 'Expand All'.
So every time you click on an individual line, it should check to see if ALL the items have now been collapsed/expanded, and if so, update the Expand/Collapse All button.
My problem is that I'm not sure how to iterate over all the items on a click to see if they are collapsed or not and properly update.
Here is a JSFiddle for this: JSFiddle
Here is my current code:
var expand = true;
jQuery.noConflict();
jQuery(function() {
jQuery('[id^=parentrow]')
.attr("title", "Click to expand/collapse")
.click(function() {
jQuery(this).siblings('#childrow-' + this.id).toggle();
jQuery(this).toggleClass("expanded collapsed");
ExpandCollapseCheck();
});
jQuery('[id^=parentrow]').each(function() {
jQuery(this).siblings('#childrow-' + this.id).hide();
if (jQuery(this).siblings('#childrow-' + this.id).length == 0)
jQuery(this).find('.expand-collapse-control').text('\u00A0');
});
jQuery('#childrow-' + this.id).hide("slide", { direction: "up" }, 1000).children('td');
});
function CollapseItems() {
jQuery('[id^=parentrow]').each(function() {
jQuery(this).siblings('#childrow-' + this.id).hide();
if (!jQuery(this).hasClass('expanded collapsed'))
jQuery(this).addClass("expanded collapsed");
});
}
function ExpandItems() {
jQuery('[id^=parentrow]').each(function() {
jQuery(this).siblings('#childrow-' + this.id).show();
if (jQuery(this).hasClass('expanded collapsed'))
jQuery(this).removeClass("expanded collapsed");
});
}
function ExpandCollapseChildren() {
if (!expand) {
CollapseItems();
jQuery('.expander').html('Expand All');
}
else {
ExpandItems();
jQuery('.expander').html('Collapse All');
}
expand = !expand;
return false;
}
function ExpandCollapseCheck() {
if ((jQuery('[id^=parentrow]').hasClass('expanded collapsed')) && (expand)) {
jQuery('.expander').html('Expand All');
CollapseItems();
expand = !expand;
}
else if ((!jQuery('[id^=parentrow]').hasClass('expanded collapsed')) && (!expand)) {
jQuery('.expander').html('Collapse All');
ExpandItems();
expand = !expand;
}
}
A couple of things I see with your code.
It seems that you may have multiple children with the same ID, such as #childrow-parent0. This is not legal HTML, and can lead to problems with JavaScript. Use classes instead.
Manipulating ID's to find children is more difficult than using built-in jQuery selectors to find children. I realize that in this case, they are siblings rather than true children, but you can still use .nextUntil(".parent") to find all of the "children" of a parent.
Use your click handlers to do the expanding/collapsing instead of repeating code. One you have a click handler, you can call .click() on a parent, and it will toggle as if you clicked it.
If half of your elements are collapsed, do you want "Expand All" or "Collapse All"? You might want both.
With all of that in mind, I wrote your code with a lot less lines. To answer your specific question, I just compared the number of '.parent.expanded' elements to the number of '.parent' elements to see if they were all expanded or not. (I changed to using a single .parent class.)
Demo
The relevant code to your question:
$('#expand_all').toggleClass("disabled", $('.parent.expanded').length == $('.parent').length);
$('#collapse_all').toggleClass("disabled", $('.parent.collapsed').length == $('.parent').length);
This uses toggleClass(), with the second argument returning true/false depending on the number of collapsed/expanded parents. This is used by toggleClass to determine whether the disabled class is applied.
Don't bother iterating, just use a selector to get a count of all the elements & their classes:
var $all = jQuery('selector to return all lines');
if($all.length == $all.filter('.collapsed').length)
//all the rows are collapsed
if($all.end().length == $all.filter('.expanded').length)
//all the rows are expanded

How to avoid the need for ctrl-click in a multi-select box using Javascript?

I thought this would be a simple hack, but I've now been searching for hours and can't seen to find the right search term. I want to have an ordinary multiple select box (<select multiple="multiple">) except I don't want the user to have to hold down the control key to make multiple selections.
In other words, I want a left click to toggle the <option> element that's under the cursor without changing any of the others. In other other words, I want something that looks like a combo list box but behaves like a group of check boxes.
Can anybody suggest a simple way to do this in Javascript? Thanks.
Check this fiddle: http://jsfiddle.net/xQqbR/1022/
You basically need to override the mousedown event for each <option> and toggle the selected property there.
$('option').mousedown(function(e) {
e.preventDefault();
$(this).prop('selected', !$(this).prop('selected'));
return false;
});
For simplicity, I've given 'option' as the selector above. You can fine tune it to match <option>s under specific <select> element(s). For ex: $('#mymultiselect option')
Had to solve this problem myself and noticed the bugged behavior a simple interception of the mousedown and setting the attribute would have, so made a override of the select element and it works good.
jsFiddle: http://jsfiddle.net/51p7ocLw/
Note: This code does fix buggy behavior by replacing the select element in the DOM. This is a bit agressive and will break event handlers you might have attached to the element.
window.onmousedown = function (e) {
var el = e.target;
if (el.tagName.toLowerCase() == 'option' && el.parentNode.hasAttribute('multiple')) {
e.preventDefault();
// toggle selection
if (el.hasAttribute('selected')) el.removeAttribute('selected');
else el.setAttribute('selected', '');
// hack to correct buggy behavior
var select = el.parentNode.cloneNode(true);
el.parentNode.parentNode.replaceChild(select, el.parentNode);
}
}
<h4>From</h4>
<div>
<select name="sites-list" size="7" multiple>
<option value="site-1">SITE</option>
<option value="site-2" selected>SITE</option>
<option value="site-3">SITE</option>
<option value="site-4">SITE</option>
<option value="site-5">SITE</option>
<option value="site-6" selected>SITE</option>
<option value="site-7">SITE</option>
<option value="site-8">SITE</option>
<option value="site-9">SITE</option>
</select>
</div>
techfoobar's answer is buggy, it unselects all options if you drag the mouse.
Sergio's answer is interesting, but cloning and removing events-bound to a dropdown is not a nice thing.
Try this answer.
Note: Doesn't work on Firefox, but works perfectly on Safari/Chrome/Opera. (I didn't test it on IE)
EDIT (2020)
After 5 years since my original answer, I think best practice here is to replace the dropdown with checkboxes. Think about it, that's the main reason why checkboxes exist in the first place, and it works nicely with old browsers like IE & modern mobiles without any custom JS to handle all the wacky scenarios.
Necromancing.
The selected answer without jQuery.
Also, it missed setting the focus when an option is clicked, because you have to do this yourself, if you write e.preventDefault...
Forgetting to do focus would affect CSS-styling, e.g. bootstrap, etc.
var options = [].slice.call(document.querySelectorAll("option"));
options.forEach(function (element)
{
// console.log("element", element);
element.addEventListener("mousedown",
function (e)
{
e.preventDefault();
element.parentElement.focus();
this.selected = !this.selected;
return false;
}
, false
);
});
I had same problem today, generally the advice is to use a list of hidden checkboxes and emulate the behavior via css, in this way is more easy to manage but in my case i don't want to modify html.
At the moment i've tested this code only with google chrome, i don't know if works with other browser but it should:
var changed;
$('select[multiple="multiple"]').change(function(e) {
var select = $(this);
var list = select.data('prevstate');
var val = select.val();
if (list == null) {
list = val;
} else if (val.length == 1) {
val = val.pop();
var pos = list.indexOf(val);
if (pos == -1)
list.push(val);
else
list.splice(pos, 1);
} else {
list = val;
}
select.val(list);
select.data('prevstate', list);
changed = true;
}).find('option').click(function() {
if (!changed){
$(this).parent().change();
}
changed = false;
});
Of course suggestions are welcome but I have not found another way
Reusable and Pure JavaScript Solution
const multiSelectWithoutCtrl = ( elemSelector ) => {
let options = document.querySelectorAll(`${elemSelector} option`);
options.forEach(function (element) {
element.addEventListener("mousedown",
function (e) {
e.preventDefault();
element.parentElement.focus();
this.selected = !this.selected;
return false;
}, false );
});
}
multiSelectWithoutCtrl('#mySelectInput') /* Can use ID or Class */
option {
font-size: 20px;
padding: 10px 20px;
}
<select multiple id="mySelectInput" class="form-control">
<option>🍎 Apple</option>
<option>🍌 Banana</option>
<option>🍍 Pineapple</option>
<option>🍉 Watermelon</option>
</select>

Categories

Resources