Only allow 2 checkboxes checked - javascript

I wanna say i rarely work with javascript. It's never been my thing and it probably never will be, so i'm sorry for my lack of knowledge to this language.. Which is why my question is so humiliating.
I found this code online:
function checkboxlimit(checkgroup, limit){
var checkgroup=checkgroup
var limit=limit
for (var i=0; i<checkgroup.length; i++){
checkgroup[i].onclick=function(){
var checkedcount=0
for (var i=0; i<checkgroup.length; i++)
checkedcount+=(checkgroup[i].checked)? 1 : 0
if (checkedcount>limit){
alert("You can check a maximum of "+limit+" boxes.")
this.checked=false
}
}
}
}
<script type="text/javascript">
checkboxlimit(document.forms.checkform.weapon[], 2)
</script>
And it looks pretty basic to me. Nothing speciel here.
But, my problem is that i'm trying to find the checkbox through the name weapon[], which i'm required to as i'm outputting multiple checkboxes as weapon[] for array's.
Now my question is, how i'll make this work with this stupid little error.
To sum up:
My problem is that my checkboxes are named weapon[] (name="weapon[]") instead of (name="weapon"), as an array, and therefor the js wont recognize it.

Are you looking for something like this?
$(':checkbox[name=weapon]').on('click',function(){
var checkedBoxlength=$(':checkbox[name=weapon]:checked').length;
if(checkedBoxlength>2){
alert('You can check a maximum of 2 boxes.');
return false;
}
});
http://jsfiddle.net/umqL3/2/
--- Edit
Just made a jquery plugin just for limiting how many chekboxes you check.
(function( $ ){
$.fn.LimitCheckableNumber = function( options ) {
var settings = $.extend( {
'nr' : 1,
}, options);
return this.each(function() {
var $this=$(this),
thisName=$this.attr('name');
$this.on('click',function(){
var count=$(":checked[name='"+thisName+"']").length;
if(count>settings.nr){
alert('You can check a maximum of '+settings.nr+' boxes. ');
return false;
};
});
});
};
})( jQuery );
Link

You can't use the [] when using dot notation. Switch to treating it like an associative array.
checkboxlimit(document.forms.checkform['weapon[]'], 2)
http://jsfiddle.net/4mYbD/3/
Works just fine.
Also, you don't need to redefine checkgroup and limit. You can just use them as the arguments.

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"));

About jquery storing local variable when writing plugin

Hi I am a newbie of using jQuery for writing plugin and currently facing a case on local variable handling,
and here is some code for all reference
<div class="apple"></div>
<div class="apple"></div>
<script>
var apple = [1,3];
$(function(){
$.fn.Hello = function(){
return this.each(function(){
var apple = 0;
$.fn.extend({
setApples:function(num_of_apples){
apple = num_of_apples
},
getApples:function(){
return apple;
}
});
});
}
var i = 0;
var $apples = $('.apple').Hello();
$apples.each(function(){
console.log(apple[i]);
$(this).setApples(apple[i]);
i++;
});
var j = 0;
$('.apple').each(function(){
console.log("Apple "+j+":"+$(this).getApples());
j++;
});
});
</script>
I would like the result be
Apple 0:1
Apple 1:3
But the last setting will always overwrite the last result.
Actual Results:
Apple 0:3
Apple 1:3
I try to change the variable from apple to this.apple, but it will return me undefined result. May I know what is the wrong part in my code?
I try to go to search from google and there is another alternative by using $(this).data('apple',value)for set and $(this).data('apple') for getting the value but since I would like to have some calculation on the number of apple e.g. apple++, it will be tedious to do get and set by this method
e.g $(this).data('apple',$(this).data('apple')+1);, so would like to ask whether there is any other alternative for this case, thank you.
Even if you did this.apple, you would still get the last item from the apple array. This is because you will be then using the prototype to store the value of apple which is shared across all jQuery objects. E.g
$.fn.extend({
apples:0,
setApples:function(num_of_apples){
this.apples = num_of_apples
},
getApples:function(){
return this.apples;
}
});
If you want to store a value for each element the quick way will be to actually store it on the element but use your extension methods to grab it.
$.fn.Hello = function(){
return this.each(function(){
$.fn.extend({
setApples:function(num_of_apples){
this.data("apple", num_of_apples)
},
getApples:function(){
return parseInt(this.data("apple"));
}
});
});
}
You could add checks to make sure its a number being passed into the setApples function but for now this should work.
also as a side note you can improve your loops
var i = 0;
$.each(function(){
i++
});
You can just use
$.each(function(index){
// use index here.
}
here is the documentation for it : https://api.jquery.com/each/
Also a working example of the apples problem here : https://jsfiddle.net/y27tn5wg/

My $.each loop is quite slow. Any methods of making it faster?

I have a chrome extension that needs to look through every <p></p> on a webpage. My chrome extension looks at the p text and checks if the text is located in an array. The problem is, the array has over 3,000 elements and I'd quite like to get that up to 12,000 or more if possible.
At the current rate, that just isn't feasible because it takes the webpage about 4 extra seconds to load the page. My chrome extension runs at the end of the document so the user can technically browse the site, it just takes 4 seconds for everything to load.
Here is my code:
$.each(arrayObj, function(key, value) {
$("p").highlight(key, {caseSensitive: true, className: 'highlight-882312', wordsOnly:true });
});
$('.highlight-882312').each(function() {
var currentKey = $(this).text();
console.log(currentKey);
//do something else here that doesn't really
//apply to the question as it only runs when the user hovers over the class.
});
and then the array looks pretty simple like this:
var arrayObj = {
"keyword1": 'keyword_1_site',
"keyword2": 'keyword_2_site',
... etc
... 3,000+ more lines ...
...
}
I'm assuming $.each isn't the most efficient, and as I said, 4 seconds to load is quite a bit. Is there anything I could do to make this more efficient? Will I ever be able to hit 12,000 lines in the array?
Thanks :)
You are running global selectors for each element in array. That's quite a lot.
So at least I would suggest to replace this:
$.each(arrayObj, function(key, value) {
$("p").highlight(key, ...);
});
by this:
var $all_p = $("p");
$.each(arrayObj, function(key, value) {
$all_p.highlight(key, ...);
});
Here's a couple of ideas :
$("p") and the .highlight() options are static, so define once, use many.
apply .highlight() only to those words that actually exist on the page.
var options = {
caseSensitive: true,
className: 'highlight-882312',
wordsOnly:true
},
arrayObj_ = {},
$p = $("p");
//Populate arrayObj_ with true for every word that exists on the page
$p.each(function() {
$(this).text().split(' ').forEach(function(word) {
arrayObj_[word] = true;
});
});
//Now loop through arrayObj_ ,
// which is hopefully much smaller than arrayObj,
// and highlight those words that are represted as keys in arrayObj.
$.each(arrayObj_, function(key, value) {
if(arrayObj[key]) {
$p.highlight(key, options);
}
});
No idea whether this will be faster or slower. You will need to run tests.
Edit ...
Even better, discover words and highlight in a single pass.
var options = {
caseSensitive: true,
className: 'highlight-882312',
wordsOnly:true
},
wordsFound = {},
$p = $("p");
$p.text().split(/\b/).forEach(function(word) {
if(!wordsFound[word]) {
wordsFound[word] = true;
if(arrayObj[word]) {
$p.highlight(word, options);
}
}
});
Try this:
var p = $("p");
$.each(arrayObj, function(key, value) {
p.highlight(key, {caseSensitive: true, className: 'highlight-882312', wordsOnly:true });
});
$('.highlight-882312').each(function(idx, element) {
var currentKey = element.text();
console.log(currentKey);
// other stuff
});
Generally speaking, you want to avoid $() selectors inside loops as they're time consuming. Notice that each()'s callback receives the element that's being iterated and you don't need to select it again.
This solution might not completely solve your problem but at least is the most efficient way to do it that I can think of without knowing more details.
Refactor them to javascript for loop instead of jQuery's each loops. https://stackoverflow.com/a/14808512/1547497
i.e.
var elems = $('.highlight-882312');
for (var i = 0; i < elems.length; ++i){
}

Need help with setting multiple array values to null in a loop - javascript

I have been working on creating a custom script to help manage a secret questions form for a login page. I am trying to make all the seperate select lists dynamic, in that if a user selects a question in one, it will no longer be an option in the rest, and so on. Anyways, the problem I am having is when I try to set the variables in the other lists to null. I am currently working with only 3 lists, so I look at one list, and find/delete matches in the other 2 lists. Here is my loop for deleting any matches.
for(i=0; i<array1.length; i++) {
if(array2[i].value == txtbox1.value) {
document.questions.questions2.options[i] = null
}
if(array3[i].value == txtbox1.value) {
document.questions.questions3.options[i] = null
}
}
This works fine if both the matches are located at the same value/position in the array. But if one match is at array1[1] and the other match is at array3[7] for example, then only the first match gets deleted and not the second. Is there something I am missing? Any help is appreciated. Thanks!
I don't see too many choices here, considering that the position in each array can vary.
Do it in separate loops, unless of course you repeat values in both arrays and share the same position
EDTI I figured out a simple solution, it may work, create a function. How about a function wich recives an array as parameter.
Something like this:
function finder(var array[], var valueToFound, var question) {
for (i=0; i<array.lenght; i++) {
if (array[i].value == valueToFound) {
switch (question) {
case 1: document.questions.questions1.options[i] = null;
break;
}
return;
}
}
}
I think i make my point, perhaps it can take you in the right direction
My bet is that the code isn't getting to array3[7] because either it doesn't exist or that array2 is too short and you're getting a JavaScript exception that's stopping the code from doing the check. Is it possible that array2 and array3 are shorter than array1?
It is more code, but I would do it like this:
var selectedvalue == txtbox1.value;
for(i=0; i<array2.length; i++) { // iterate over the length of array2, not array1
if(array2[i].value == selectedvalue) {
document.questions.questions2.options[i] = null;
break; // found it, move on
}
}
for(i=0; i<array3.length; i++) {
if(array3[i].value == selectedvalue) {
document.questions.questions3.options[i] = null;
break; // you're done
}
}

javascript - search multiple html #ID and pass to function

I've got an order form, to which I can append fields by clicking a button. I've got some back end javascript running which totals up the order price, but the grand total script is eluding me.
My problem is that I need the script to seach the entire DOM and find how many have an ID which matches the following pattern.
totprice01
totprice02
totprice03
totprice(n)
I've been playing with this regex, but with not a lot of luck i'm afraid:
matchStr = new RegExp("\\btotprice\\d{2}\\b", "gi");
Once i've got an array of all the HTML IDs I need to pass them into a function which so far looks like this - notice it's all hard coded, not in the least dynamic:
document.getElementById('totpricetot').value = Math.round((parseFloat(document.getElementById('totprice1').value)+parseFloat(document.getElementById('totprice2').value)+parseFloat(document.getElementById('totprice3').value)+parseFloat(document.getElementById('totprice4').value)+parseFloat(document.getElementById('totprice5').value)+parseFloat(document.getElementById('totprice6').value)+parseFloat(document.getElementById('totprice7').value)+parseFloat(document.getElementById('totprice8').value)+parseFloat(document.getElementById('totprice9').value)+parseFloat(document.getElementById('totprice10').value))*100)/100;
Is anyone able to help me put this into expression + function to return the sum of all the values into ?
Thanks a lot!
EDIT
OK I decided to ditch just using plain ol' javascript - JQuery it is! I've put together this code using some of the examples below, but can someone help me debug it I keep get "not defined" errors from the debugger - it appears this function is unavailable to the rest of the DOM?
<input id="totprice08" onChange="totChange()" class="total_field" />
<input id="totprice09" onChange="totChange()" class="total_field" />
<input id="totprice10" onChange="totChange()" class="total_field" />
etc...
<input id="totpricetot" value="0.00" name="totpricetot" />
jQuery(function($) {
function totChange() {
var sum=0;
$('.total_field').each(function() {
sum += $( this ).val () * 1;
} );
$('#totpricetot').val(sum);
}
});
I love it how every javascript question on SO is answered by, "use jQuery"...
Anyways...you can do it with just plain ol' javascript too:
var inputs = document.getElementsByTagName("input");
var priceInputs = [];
for (var i=0, len=inputs.length; i<len; i++) {
if (inputs[i].tagName.indexOf("totprice") > -1) {
priceInputs[priceInputs.length] = parseInt(inputs[i].value);
}
}
calcTotal(priceInputs);
Then just create a function to loop through the array and sum up (:
There are lots of solutions. Besides giving all the elements in question the same class name and use jQuery (or something similar), you could also simply remember the total number of the elements in some JavaScript variable. Would it be possible? I know - it’s kind of an old school solution. You have not indicated that you are using jQuery or any other JavaScript library, so it may be more suitable for you.
Edit: Just to be more explicit:
// your variable, updated every time you add/remove a new field
var fieldsCount;
// whenever you need to do anything with all the fields:
for (var i = 0; i < fieldsCount; i++)
{
var field = document.getElementById("totprice" + i);
// ...
}
Using jQuery, you could select and sum the elements like this:
var sum = 0;
$('[id^=totprice-]').each(function()
{
sum += $(this).val();
});
Give the inputs you need to sum up a class and then get those inputs by that class name (jQuery or some other framework would come in handy here).
if you would use jQuery you could do something like this:
function calculateSum () {
var sum = 0;
$( '.YourClass' ).each ( function () {
sum += $( this ).val () * 1;
} );
return sum;
}
You could give all your total price elements the same CSS class and then get all elements of this class (nice and easy with JQuery).
Give each element a class and use jQuery.

Categories