remove element from array when checkbox unchecked - javascript

I add an element to an array when the checkbox is checked and I need to remove it when it is unchecked. I use splice to remove an element. I just can't seem to call an event when it's unchecked. I tried using this:
if ($('input[name="'+category+'"]:checked'))
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
It adds the needed element ok, when the checkbox is checked, but it doesn't seem to remove it when it's unchecked. Variable category is a loop variable and is correct.
If someone can work this out, it would be greatly appreciated.

jQuery selectors always return an object whether an element is matched or not.
What you've effectively got is:
if (new Object())
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
Objects are always truthy (no matter if it's an empty object, or an object John Resig initialized), so this if statement will never execute the else.
What you're probably after is:
if ($('input[name="'+category+'"]:checked').length)
item_id[category] = $(this).attr("id");
else
item_id.splice(category, 1);
Which checks the length property instead.
This still won't work however, as splice() will shift all elements in your array; making the category wrong.
If your binding the event on a number of checkbox elements, it will be unwise to use .bind() (and it's counterparts .click()), as this method will bind an event for each checkbox. Instead, use .live(), or .delegate(); this will bind one event to an ancestor of all checkbox elements, and listen for event (using JavaScripts event bubbling), which is much more efficient.
Taking both of these points into consideration, you might fancy something like this.
$(yourJquerySelector).live('change', function () {
var category = '?' // (this.value ?)
item_id[category] = this.checked ? this.id : undefined;
});

Not entirely sure what you are after to be honest, but here is my solution to this,hope it works for you in some way
Javascript Array - indexOf Method:
http://www.tutorialspoint.com/javascript/array_indexof.htm
<script>
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
$(function() {
var checkedItems = new Array();
$(":checkbox").change(function () {
if($(this).attr('checked'))
{
checkedItems.push($(this).attr("id"));
}
else
{
var index = checkedItems.indexOf($(this).attr("id"));
checkedItems.splice(index,1);
}
});
});
</script>
HTML
<input type="checkbox" id="c1" value="1">
<input type="checkbox" id="c2" value="2">
<input type="checkbox" id="c3" value="3">

The splice function is meant to return what's removed, so start debugging by displaying its return value.

Change your if condition to:
$('input[name="'+category+'"]').is(':checked')
As mentioned by Matt, your current if condition is a selector that returns a list of jQuery elements. Testing the number of elements returned (using the length property) would also do the trick.

Related

.hide() is not a function error when executing from a loop

I want to be able to loop over a few different labels and hide their content based on if a radio button is check or not. This is the solution I came up with, but I keep getting an error in the console.
var hazardOptions = $(".js-hazardous-option");
var hazard = $("input[name=Hazardous]");
for (var i = 0, len = hazard.length; i < len; i++) {
if (hazard[i].id === "HazardousYes" && hazard[i].checked) {
for (var ii = 0, length = hazardOptions.length; ii < length; ii++) {
hazardOptions[ii].show();
}
} else if (hazard[i].id === "HazardousNo" && hazard[i].checked) {
for (var iii = 0, leng = hazardOptions.length; iii < leng; iii++) {
hazardOptions[iii].hide();
}
}
}
The error I get is:
hide() is not a function
Not sure what I'm missing, I've tried having a look online for a similar issue, but with no luck. I'm pretty sure that the problem is here: hazardOptions[iii].hide(); but not really sure why and/or how to fix it.
When you have a list of objects from a JQuery selector, if you try to access them via index you actually get the DOM element back and not the JQuery object. It's confusing for sure but it is in the documentation.
What you effectively need to do is turn it back into a JQuery object:
$(hazardOptions[iii]).hide();
Or you can use the eq() function with does provide the JQuery object ad thus still has the hide() function:
hazardOptions.eq(iii).hide();
Most probably you need to wrap it with $
$(hazardOptions[ii]).hide()
As you currently have it, if hazard.id === "HazardousYes", you are showing all hazardOptions, and if it is "HazardousNo"you are hiding all of them.
You can call .show() and .hide() on a jQuery collection and it will apply that to all elements in the collection. The below code will replicate the logic of your original code, however, the hazardOptions final show/hide state will be solely determined by the last hazard that is checked and has an id equal to "HazardousYes" and "HazardousNo". This may be what you want, but I would imagine it's not.
var hazardOptions = $(".js-hazardous-option");
var hazards = $("input[name=Hazardous]");
hazards.each(function (index, hazard) {
if (hazard.checked) {
if (hazard.id === "HazardousYes") {
hazardOptions.show();
} else if (hazard.id === "HazardousNo") {
hazardOptions.hide();
}
}
}
Edit - Come to think of it, if you don't have elements with duplicate IDs, You can make this really simple:
hazardOptions.show($("#HazardousYes").is(":checked"));
hazardOptions.hide($("#HazardousNo").is(":checked"));

JQuery is(":focus")

It seems that for some reason, I cannot perform:
$(".exampleClass")[0].is(":focus");
It tells me - TypeError: undefined is not a function.
What I am trying to do is grab a few elements with jquery, scan through them, and find which one is focused (so that I can focus the next element in the array programmatically).
var fields = $(".textField");
var selected = false;
for(var j = 0; j < fields.length; j++){
var field = fields[j];
console.log(field);
if(selected){
field.focus();
}else if(field.is(':focus') && !selected ){
selected = true;
}
}
It all works fine until field.is(':focus') Why won't this work?
When you index into the jQuery object with the [ ] operator, you extract the underlying component of the list of matched elements. That component will be a DOM node, and it won't have a .is() method.
If you coded it like
$(".exampleClass").eq(0).is(":focus");
you'd be working with a jQuery object, and you wouldn't have the problem.

GetElementsByClassName Not Working As Expected [duplicate]

This question already has answers here:
What do querySelectorAll and getElementsBy* methods return?
(12 answers)
Closed 8 years ago.
I have a table that typically looks like this on the screen:
The multiple rooms are displayed by using a foreach loop.
Now I need to disable all the second dropdown boxes when a value has been selected in one of the first ones, or vice versa.
Typical code for one of the dropdown boxes is
<select onchange="std()" class="numrooms" name="numrooms[4]">
<option value="" selected>Select</option>
<option value="1"> 1</option>
<option value="2"> 2</option>
</select>
I am using the following javascript:
function std() {
d = document.getElementsByClassNames("numrooms").value;
if (d>0) {
document.getElementsByClassNames('numrooms_nr').disabled = true;
}else{
document.getElementsByClassNames('numrooms_nr').disabled = false;
}
}
function nr() {
e = document.getElementsByClassNames("numrooms_nr").value;
if (e>0) {
document.getElementsByClassNames('numrooms').disabled = true;
}else{
document.getElementsByClassNames('numrooms').disabled = false;
}
}
but it doesn't work.
I have tried changing the classes to IDs and then using GetElementById() in the script and that does work, but of course it only works on one pair of dropdowns. I thought going to classes and using Get ElementsBy ClassName() would do the trick, but apparently not.
Am I missing something obvious? Or doing it completely wrong?
EDIT
As everyone pointed out, I wrote "getElementsByClass" in the question when it should have been "getElementsByClassName". However that was a mistake when I wrote the question and not in my actual code. I've corrected it here now.
EDIT2
I'm getting there, but not quite fully sorted yet. I've adopted #Notulysses suggestion so for testing purpose my script is
function std() {
d = document.getElementsByClassName('numrooms')[1].value;
if (d>0) {
var n = document.getElementsByClassName('numrooms_nr')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
}else{
var n = document.getElementsByClassName('numrooms_nr')
for(var i=0;i<n.length;i++){
n[i].disabled = false;
}
}
}
function nr() {
e = document.getElementsByClassName('numrooms_nr')[0].value;
if (e>0) {
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
}else{
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = false;
}
}
}
function(std) now disables all of the second dropdown boxes when the first dropdown in the second room is selected (because I have set it to 1). Similarly function(nr) disables all of the first dropdown boxes (because I have set it to [0]).
But how do I disable all the second dropdowns when any of the first dropdowns is selected?
You are using getElementsByClass (it doesn't exist) and changing property for the whole collection (not valid, you should iterate through Node list to change attribute's value). You should do something like this :
var n = document.getElementsByClassName('numrooms')
for(var i=0;i<n.length;i++){
n[i].disabled = true;
}
Its GetElementsByClassName not GetElementsByClass and it returns you NodeList of nodes so if you want to change any property you need to use indexing, i.e, looping:
document.getElementsByClassName('numrooms_nr')[0].disabled = true;
and here is your complete code:
var d = document.getElementsByClassNames("numrooms");
for(var i=d.length-1;i>=0; i--){
if(n[i].value > 0){
n[i].disabled = true;
}
else{
n[i].disabled = false;
}
}
You are using it wrong. It's not getElementsByClass , it is getElementsByClassName. And it returns a HTMLCollection of found elements. To have an access to any element you should use indexing.
document.getElementsByClassName('someclass')[0] <- index
See the link for more details -> Link
getElementsByClassName and getElementsByTagName do not return a single element, like get ElementById. Rather, they return an array containing all the elements with that class. This has tripped up many Javascripters over time. Also note that getElementsByClassName won't work in early IE versions (surprise surprise!)
As such, you are missing the bit with the [0] or [1] or the [2] etc. after getElementsByClassName is written, for example:
document.getElementsByClassName("numrooms")[0]
will refer to the first of the bunch with that class name.

Loop a jQuery selection in order of tab-index

So I have a varying amount of form inputs, and depending on how the app is set up in the CMS, they can be on different 'pages' (just show/hide pages within the same document). Which means that their tab-index doesn't necessarily follow the DOM structure.
They're also varying form types.
How would I go about looping through (validating) in order of the tab-index?
(note: the tab-index doesn't always follow an incremental pattern, as the 'next' button on one of the show/hide buttons also has a tab-index)
I have thought about something like this:
var $inputs = $('input[type="text"], select, input[type="radio"]'),
numInputs = $inputs.length,
numInputsChecked = 0,
tabIndex = 0;
while(numInputs != numInputsChecked){
var $input = $inputs.filter(function(){
return $(this).attr("tabindex") == tabIndex;
});
if($input.length){
// Do validation code
numInputsChecked++;
}
tabIndex++;
}
but I believe there should be a better way of achieving this task. (note, I haven't actually tested this code, I'm just attempting to illustrate what I am thinking)
This approach will do it, but there may be a more elegant way (I didn't put much time into this):
HTML:
<input type="text" tabindex="2" />
<select tabindex="4"></select>
<input type="text" tabindex="1" />
<input type="text" tabindex="3" />
JS:
/**
* Sort arrays of objects by their property names
* #param {String} propName
* #param {Boolean} descending
*/
Array.prototype.sortByObjectProperty = function(propName, descending){
return this.sort(function(a, b){
if (typeof b[propName] == 'number' && typeof a[propName] == 'number') {
return (descending) ? b[propName] - a[propName] : a[propName] - b[propName];
} else if (typeof b[propName] == 'string' && typeof a[propName] == 'string') {
return (descending) ? b[propName] > a[propName] : a[propName] > b[propName];
} else {
return this;
}
});
};
$(function(){
var elms = [];
$('input, select').each(function(){
elms.push({
elm: $(this),
tabindex: parseInt($(this).attr('tabindex'))
})
});
elms.sortByObjectProperty('tabindex');
for (var i = 0; i < elms.length; i++) {
var $elm = elms[i].elm;
console.log($elm.attr('tabindex'));
}
});
jQuery selectors return an array of elements in DOM order by default. See http://docs.jquery.com/Release%3AjQuery_1.3.2.
However, you could add custom selector behaviour by extending jQuery's default selector, see: http://james.padolsey.com/javascript/extending-jquerys-selector-capabilities/,
sorting your array of selected inputs using a technique similar to this plugin. Remember to ignore the parts that actually rearrange the elements.
As said, sorting objects is kind of none-sense ; Anyway, still you can use Array prototype. ...
Does your needs worth writting all that code ?
What do you mean by "better way " ?
-> choose the less greedy method (functions calls, caching objects etc..)
I think your code is okey, may be you could optimize your code and get ride off checked inputs :
var $input = $inputs.filter(function(){
return $(this).attr("tabindex") == tabIndex;
});
// Preventing your code from processing this element upon next loop if any.
$inputs = $inputs.not($input);
But this really makes sense while processing on hudge node collections ...

How to go over all inputs that have a custom attribute

I have a form with checkboxes.
All of the checkboxes have an attribute called att, which contains several numbers that are separated by commas.
I'd like to create a function that gets a number, goes over all the checkboxes and checks all the checkboxes that their att attribute contains that number.
Can please someone point me at the right direction?
Using jQuery you can do something like
function myfunc(num)
{
$(":checkbox[att!='']").each(function()
{
var i, l, values = $(this).attr("att").split(",");
for (i = 0, l = values.length; i < l; ++i)
{
if (values[i] == num)
{
$(this).attr("checked", "checked");
break;
}
}
});
}
With JQuery you would use the attribute-contains selector to get all elements where an attribute contains a certain value.
$('input[att*="100"]').val('input has 100 in it!');
This loops over all input elements and gives you an array containing the values of att (split using the comma), so I'd add some logic to pick out only checkboxes:
for (var i = 0; i < document.getElementsByTagName('input'); i++)
{
var att_array = document.getElementsByTagName('input')[i].getAttribute('att').split(',');
}
This will get you all inputs with the attribute ATTR and then alerts the val of each of those. You can, of course, do whatever you want with the val when you have it.
$("input[ATTR]").each(function(){
alert($(this).attr("ATTR"))
})
If you want to limit this to checkboxes, change the selector as is shown below
$(":checkbox[ATTR]").each(function(){
alert($(this).attr("ATTR"))
})

Categories