Reputation: 107
I fill the table with a ADODB.Recordset. I have 25 rows. But it can change. And last column is checkboxes.
Only one checkbox can select. Check one and uncheck others automatic.
<input class="cb" id="txtdurum_<%=counter%>" name="txtdurum" type="checkbox" />
Help please :)
Upvotes: 0
Views: 3101
Reputation: 253308
While I would – strongly – recommend that you use <input>
elements of type="radio"
(with a 'choose nothing' option, if that's required), I was intrigued enough to spend the last few hours putting together this approach.
This is one way of doing it, it feels little over-the-top and it's not quite as streamlined as I'd like, but it does suffice to meet your requirements. I've provided a simple demo for the introduction, and the JavaScript is commented throughout:
var Limit = (function () {
// private to this function, but shared by all instances:
// simple 'closest' function;
// start: DOM Node,
// needle: String, the tagName of the ancestor to find.
// if no ancestor exists (we stop at the <body> element)
// function returns null:
var closest = function (start, needle) {
// we begin at the 'start' node, but use
// the 'current' variable as a pointer as
// we move through the ancestors:
var current = start,
// converting the tagName to lower-case,
// for predictable comparisons:
find = needle.toLowerCase();
// while the tagName of the current element-node is not
// what we're looking for AND the current element-node is
// not the <body> element:
while (current.tagName.toLowerCase() !== find && current.tagName.toLowerCase() !== 'body') {
// we set the current node to its parentNode,
// thereby ascending through the DOM:
current = current.parentNode;
}
// if the tagName of the current element-node is 'body'
// we return null, otherwise we return that current node:
return current.tagName.toLowerCase() === 'body' ? null : current;
};
return function (groupSelector) {
// protected variables, available on a
// per-instance basis:
// the array in which we'll hold the selected
// <input> elements as they're checked:
var checked = [],
// the group of elements to which this instance
// will apply:
group = document.querySelectorAll(groupSelector),
// the defaults:
// allowInvalidity, Boolean:
// true: allows the user to select more than
// the maximum number of choices.
// false: prevents the selection of options
// beyond the maxumum number.
// fifo, Boolean:
// true: should the user try to select more
// than the maximum number of choices
// the first element in the array is
// removed.
// false: should the user try to select more
// than the maximum number of choices
// subsequent choices are prevented.
// maxChoices, Number:
// defines the maximum number of choices
// that can be made.
// parentErrorClass, String:
// the class-name to be applied to the
// parent of the <input> elements when
// the user chooses more than the maximum
// number of options (requires
// settings.invalidity to be true).
defaults = {
'allowInvalidity': false,
'fifo': true,
'maxChoices': 1,
'parentErrorClass': 'error'
};
// the object containing the function(s) we
// make available:
return {
// opts is the user-defined settings
// passed in:
'nOnly': function (opts) {
// a simple, though potentially costly,
// means of avoiding JavaScript's
// pass-by-reference (which prevents
// settings = dafaults from working),
// here creating a copy via the JSON functions:
var settings = JSON.parse(JSON.stringify(defaults));
// iterating over the user-defined settings in the
// supplied opts object:
for (var setting in opts) {
// avoiding iterating over inherited properties
// from the Object.prototype:
if (opts.hasOwnProperty(setting)) {
// setting the settings options to
// those supplied in the opts object:
settings[setting] = opts[setting];
}
}
// iterating over the Array-like NodeList returned by
// document.querySelectorAll() (when we retrieved the
// nodes for this 'group'), using Function.prototype.call()
// to apply Array.prototype.forEach():
Array.prototype.forEach.call(group, function (input) {
// there are three arguments available to
// Array.prototype.forEach(), the names are
// user-defined (within variable-naming constraints);
// the first (here: 'input') the current array-element
// from the array over which we're iterating,
// the second (not used) is the index of that array-element,
// the third (not used) is the full array over which
// we iterate.
// here we bind the anonymous function as the 'change'
// event-handler on each of the <input> elements in
// the parent group:
input.addEventListener('change', function (event) {
if (input.checked && settings.allowInvalidity === false) {
// add the <input> to the checked array:
checked.push(input);
// if too many choices have been made:
if (checked.length > settings.maxChoices) {
// we call Array.prototype.pop() (if
// settings.fifo is true) or
// Array.prototype.shift() (if
// settings.fifo is not exactly-true)
// to select the first element of the
// checked Array (shift()) or the last
// element (pop()) and set that element's
// checked property to false. Using shift()
// or pop() also removes it from the array:
checked[settings.fifo === true ? 'shift' : 'pop']().checked = false;
}
} else if (input.checked && settings.allowInvalidity === true) {
// we simply add the <input> to the array:
checked.push(input)
} else {
// we test that the <input> element is in
// the checked Array:
if (checked.indexOf(input) > -1) {
// using Array.prototype.splice() to
// remove it from the Array; using
// Array.prototype.indexOf() (again)
// to retrieve its index in the Array:
checked.splice(checked.indexOf(input), 1);
}
}
// iterating over the group, with Array.prototype.forEach():
Array.prototype.forEach.call(group, function (input) {
// if the <input> is not checked, or the number of choices
// is less than the permitted maximum:
if (!input.checked || checked.length <= settings.maxChoices) {
// we remove the parentErrorClass from the parentNode:
input.parentNode.classList.remove(settings.parentErrorClass);
// otherwise if the <input> is checked, AND
// there are too many choices:
} else if (input.checked && checked.length > settings.maxChoices) {
// we add the parentErrorClass to the parentNode:
input.parentNode.classList.add(settings.parentErrorClass);
}
});
});
});
}
};
};
})();
new Limit('.group1 input').nOnly({
'allowInvalidity': true,
'maxChoices': 3
});
new Limit('.group2 input').nOnly({
'maxChoices': 1,
'fifo': false
});
new Limit('.group3 input').nOnly({
'allowInvalidity': true,
'maxChoices' : 2,
'parentErrorClass' : 'oops'
});
var Limit = (function() {
var closest = function(start, needle) {
var current = start,
find = needle.toLowerCase();
while (current.tagName.toLowerCase() !== find && current.tagName.toLowerCase() !== 'body') {
current = current.parentNode;
}
return current.tagName.toLowerCase() === 'body' ? null : current;
};
return function(groupSelector) {
var checked = [],
group = document.querySelectorAll(groupSelector),
defaults = {
'allowInvalidity': false,
'fifo': true,
'maxChoices': 1,
'parentErrorClass': 'error'
};
return {
'nOnly': function(opts) {
var settings = JSON.parse(JSON.stringify(defaults));
for (var setting in opts) {
if (opts.hasOwnProperty(setting)) {
settings[setting] = opts[setting];
}
}
Array.prototype.forEach.call(group, function(input) {
input.addEventListener('change', function(event) {
if (input.checked && settings.allowInvalidity === false) {
checked.push(input);
if (checked.length > settings.maxChoices) {
checked[settings.fifo === true ? 'shift' : 'pop']().checked = false;
}
} else if (input.checked && settings.allowInvalidity === true) {
checked.push(input)
} else {
if (checked.indexOf(input) > -1) {
checked.splice(checked.indexOf(input), 1);
}
}
Array.prototype.forEach.call(group, function(input) {
if (!input.checked || checked.length <= settings.maxChoices) {
input.parentNode.classList.remove(settings.parentErrorClass);
} else if (input.checked && checked.length > settings.maxChoices) {
input.parentNode.classList.add(settings.parentErrorClass);
}
});
});
});
}
};
};
})();
new Limit('.group1 input').nOnly({
'allowInvalidity': true,
'maxChoices': 3
});
new Limit('.group2 input').nOnly({
'maxChoices': 1,
'fifo': false
});
new Limit('.group3 input').nOnly({
'allowInvalidity': true,
'maxChoices': 2,
'parentErrorClass': 'oops'
});
form {
-moz-column-count: 3;
-webkit-column-count: 3;
column-count: 3;
-moz-break-inside: avoid-column;
-webkit-break-inside: avoid-column;
break-inside: avoid-column;
}
label {
display: block;
border: 2px solid transparent;
transition: all 0.5s linear;
}
.error {
border: 2px solid #f00;
}
.oops {
background-color: #f90;
}
<form action="#" method="post">
<fieldset>
<legend>Group 1</legend>
<label class="group1">input 1
<input type="checkbox" />
</label>
<label class="group1">input 2
<input type="checkbox" />
</label>
<label class="group1">input 3
<input type="checkbox" />
</label>
<label class="group1">input 4
<input type="checkbox" />
</label>
<label class="group1">input 5
<input type="checkbox" />
</label>
</fieldset>
<fieldset>
<legend>Group 2</legend>
<label class="group2">input 1
<input type="checkbox" />
</label>
<label class="group2">input 2
<input type="checkbox" />
</label>
<label class="group2">input 3
<input type="checkbox" />
</label>
<label class="group2">input 4
<input type="checkbox" />
</label>
<label class="group2">input 5
<input type="checkbox" />
</label>
</fieldset>
<fieldset>
<legend>Group 3</legend>
<label class="group3">input 1
<input type="checkbox" />
</label>
<label class="group3">input 2
<input type="checkbox" />
</label>
<label class="group3">input 3
<input type="checkbox" />
</label>
<label class="group3">input 4
<input type="checkbox" />
</label>
<label class="group3">input 5
<input type="checkbox" />
</label>
</fieldset>
</form>
External JS Fiddle demo, for experimentation.
To apply this to your own situation, the function can be called like so:
// here, the only difference is the CSS selector
// passed to the Constructor:
new Limit('table input[type=checkbox]').nOnly({
'maxChoices': 1
});
var Limit = (function() {
var closest = function(start, needle) {
var current = start,
find = needle.toLowerCase();
while (current.tagName.toLowerCase() !== find && current.tagName.toLowerCase() !== 'body') {
current = current.parentNode;
}
return current.tagName.toLowerCase() === 'body' ? null : current;
};
return function(groupSelector) {
var checked = [],
group = document.querySelectorAll(groupSelector),
defaults = {
'allowInvalidity': false,
'fifo': true,
'maxChoices': 1,
'parentErrorClass': 'error'
};
return {
'nOnly': function(opts) {
var settings = JSON.parse(JSON.stringify(defaults));
for (var setting in opts) {
if (opts.hasOwnProperty(setting)) {
settings[setting] = opts[setting];
}
}
Array.prototype.forEach.call(group, function(input) {
input.addEventListener('change', function(event) {
if (input.checked && settings.allowInvalidity === false) {
checked.push(input);
if (checked.length > settings.maxChoices) {
checked[settings.fifo === true ? 'shift' : 'pop']().checked = false;
}
} else if (input.checked && settings.allowInvalidity === true) {
checked.push(input)
} else {
if (checked.indexOf(input) > -1) {
checked.splice(checked.indexOf(input), 1);
}
}
Array.prototype.forEach.call(group, function(input) {
if (!input.checked || checked.length <= settings.maxChoices) {
input.parentNode.classList.remove(settings.parentErrorClass);
} else if (input.checked && checked.length > settings.maxChoices) {
input.parentNode.classList.add(settings.parentErrorClass);
}
});
});
});
}
};
};
})();
new Limit('table input[type=checkbox]').nOnly({
'maxChoices': 1
});
<table>
<tr>
<td>row 1</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 2</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 3</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 4</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 5</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 6</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 7</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 8</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 9</td>
<td>
<input type="checkbox" />
</td>
</tr>
<tr>
<td>row 10</td>
<td>
<input type="checkbox" />
</td>
</tr>
</table>
External JS Fiddle demo, for experimentation.
This is, obviously, a representative demonstration since you neglected to provide the relevant (mcve) HTML, however since it relies only on an appropriate selector it should be easily applicable to your situation.
References:
Array.prototype.forEach()
.Array.prototype.indexOf()
.Array.prototype.push()
.Array.prototype.shift()
.Array.prototype.splice()
.assesment ? ifTrue : ifFalse
).document.querySelector()
.document.querySelectorAll()
.Element.classList
API.EventTarget.addEventListener()
.for...in
loop.Function.prototype.call()
.JSON.parse()
.JSON.stringify()
.Node.parentNode
.Node.tagName
.Object.prototype.hasOwnProperty()
.String.prototype.toLowerCase()
.Upvotes: 0
Reputation: 321
You can use jQuery:
$('.cb').change(function(){
$('.cb').prop('checked',false);
$(this).prop('checked',true);
});
This adds a change-listener to all checkboxes with the "cb" class-Attribute. Place that code-fragment into the jquery-ready-function.
Example: JSFiddle
UPDATE1: If you also want to enable, that the user can uncheck all:
$('.cb').change(function(){
var checkState = $(this).prop('checked');
$('.cb').prop('checked',false);
$(this).prop('checked', checkState);
});
Example: JSFiddle2
But: better use radiobuttons
Upvotes: 1
Reputation: 8488
Try this code. Add an onchange
event to each input checkbox.
<input class="cb" id="txtdurum_<%=counter%>" name="txtdurum" type="checkbox" onchange="check(this)"/>
JS:
function check(element){
if(element.checked){
var checkboxes = document.getElementsByClassName('cb');
for(var i=0;i<checkboxes.length;i++){
if(checkboxes[i]!=element)
checkboxes[i].checked = false;
}
}
}
Example Fiddle.
Upvotes: 1