How to stop refreshing the select box option after pressing OK button - html

I have created a UI Page in Servicenow below is my simple HTML snipped that creates a Select box and a OK button
Now i selected the select box as Mango and i typed ok, once i click on ok it is setting the value but when i refresh the browser it going to back to previous view. how to keep the same option which user select until user changes it
function validation(){
var value=document.getElementById("selectedValue").value;
if(value=='disabled selected')
{
alert("Please select any value before submitting");
}
else if(value=="1"){
alert("hello this is mango");
}
}
<div class="container">
<div class="row">
<div class="form-group" style="margin-top:12px;">
<select class="form-control" name="selectedValue" id="selectedValue" style="width:150px;">
<option value="disabled selected">Choose your option</option>
<option value="1" >Mango</option>
<option value="2">Orange</option>
<option value="3">Grapes</option>
</select>
<!--<h4 id="number_of_updates" style="display:none"><span class="label label-danger"></span></h4> -->
</div>
<button type="button" class="btn btn-primary" onclick="validation()" style="padding: 0px 8px;">Ok</button>
</div>
</div>
It is working as required, when i refresh the browser it will be set to Choose your option. Can anyone please help me how to save the selected value till it is changed

To achieve the desired result I would use localStorage.
I would set localStorage item inside your validation() function:
function validation(){
var value=document.getElementById("selectedValue").value;
localStorage.setItem('MyValue', value);
if(value=='disabled selected')
{
alert("Please select any value before submitting");
}
else if(value=="1"){
alert("hello this is mango");
}
}
Note, that I'm setting it to selected value from selectList.
Then we need to retrieve this saved value on page load(in case someone refreshes the page):
window.onload = function (){ //function that is called when your page
var selectList = document.getElementById('selectedValue');
var stored = localStorage.getItem('MyValue');
console.log(stored);
if(stored && selectList){
selectList.value = stored //setting saved value to as selected in selectList
}
}
EDIT: Even though localStorage is supported by majority of browsers, its is recommended to check if its accessible. Like if (typeof localStorage !== 'undefined') ...

There's several ways to keep state across refresh. localStorage is obvious choice but maybe an easier solution for your case may be encoding the state in the URL like this (basically the same way web apps keep some of their state).
Insert this code at the end inside validation() function:
history.pushState(null, '', '?val=' + value);
Insert this code after that function:
window.onload = function() {
var params = new URLSearchParams(location.search);
var value = params.get('val') || 'disabled selected';
var select = document.getElementById('selectedValue');
if (select) select.value = value;
}
That's it. Notes: URLSearchParams doesn't work on IE, instead it's not hard to parse the query by hand.

Related

Java web: Load another data into second <select> when selection changed for first <select> [duplicate]

Suppose I am having three dropdownlist controls named dd1, dd2 and dd3. The value of each dropdownlist comes from database. dd3's value depends upon value of dd2 and dd2's value depends on value of dd1. Can anyone tell me how do I call servlet for this problem?
There are basically three ways to achieve this:
Submit form to a servlet during the onchange event of the 1st dropdown (you can use Javascript for this), let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database as a Map<String, String>, let it store them in the request scope. Finally let JSP/JSTL display the values in the 2nd dropdown. You can use JSTL (just drop jstl-1.2.jar in /WEB-INF/lib) c:forEach tag for this. You can prepopulate the 1st list in the doGet() method of the Servlet associated with the JSP page.
<select name="dd1" onchange="submit()">
<c:forEach items="${dd1options}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd2" onchange="submit()">
<c:if test="${empty dd2options}">
<option>Please select parent</option>
</c:if>
<c:forEach items="${dd2options}" var="option">
<option value="${option.key}" ${param.dd2 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd3">
<c:if test="${empty dd3options}">
<option>Please select parent</option>
</c:if>
<c:forEach items="${dd3options}" var="option">
<option value="${option.key}" ${param.dd3 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
Once caveat is however that this will submit the entire form and cause a "flash of content" which may be bad for User Experience. You'll also need to retain the other fields in the same form based on the request parameters. You'll also need to determine in the servlet whether the request is to update a dropdown (child dropdown value is null) or to submit the actual form.
Print all possible values of the 2nd and 3rd dropdown out as a Javascript object and make use of a Javascript function to fill the 2nd dropdown based on the selected item of the 1st dropdown during the onchange event of the 1st dropdown. No form submit and no server cycle is needed here.
<script>
var dd2options = ${dd2optionsAsJSObject};
var dd3options = ${dd3optionsAsJSObject};
function dd1change(dd1) {
// Fill dd2 options based on selected dd1 value.
var selected = dd1.options[dd1.selectedIndex].value;
...
}
function dd2change(dd2) {
// Fill dd3 options based on selected dd2 value.
var selected = dd2.options[dd2.selectedIndex].value;
...
}
</script>
<select name="dd1" onchange="dd1change(this)">
<c:forEach items="${dd1options}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select name="dd2" onchange="dd2change(this)">
<option>Please select parent</option>
</select>
<select name="dd3">
<option>Please select parent</option>
</select>
One caveat is however that this may become unnecessarily lengthy and expensive when you have a lot of items. Imagine that you have 3 steps of each 100 possible items, that would mean 100 * 100 * 100 = 1,000,000 items in JS objects. The HTML page would grow over 1MB in length.
Make use of XMLHttpRequest in Javascript to fire an asynchronous request to a servlet during the onchange event of the 1st dropdown, let the servlet get the selected item of the 1st dropdown as request parameter, let it obtain the associated values of the 2nd dropdown from the database, return it back as XML or JSON string. Finally let Javascript display the values in the 2nd dropdown through the HTML DOM tree (the Ajax way, as suggested before). The best way for this would be using jQuery.
<%# page pageEncoding="UTF-8" %>
<%# taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html lang="en">
<head>
<title>SO question 2263996</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
$(document).ready(function() {
$('#dd1').change(function() { fillOptions('dd2', this); });
$('#dd2').change(function() { fillOptions('dd3', this); });
});
function fillOptions(ddId, callingElement) {
var dd = $('#' + ddId);
$.getJSON('json/options?dd=' + ddId + '&val=' + $(callingElement).val(), function(opts) {
$('>option', dd).remove(); // Clean old options first.
if (opts) {
$.each(opts, function(key, value) {
dd.append($('<option/>').val(key).text(value));
});
} else {
dd.append($('<option/>').text("Please select parent"));
}
});
}
</script>
</head>
<body>
<form>
<select id="dd1" name="dd1">
<c:forEach items="${dd1}" var="option">
<option value="${option.key}" ${param.dd1 == option.key ? 'selected' : ''}>${option.value}</option>
</c:forEach>
</select>
<select id="dd2" name="dd2">
<option>Please select parent</option>
</select>
<select id="dd3" name="dd3">
<option>Please select parent</option>
</select>
</form>
</body>
</html>
..where the Servlet behind /json/options can look like this:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String dd = request.getParameter("dd"); // ID of child DD to fill options for.
String val = request.getParameter("val"); // Value of parent DD to find associated child DD options for.
Map<String, String> options = optionDAO.find(dd, val);
String json = new Gson().toJson(options);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
Here, Gson is Google Gson which eases converting fullworthy Java objects to JSON and vice versa. See also How to use Servlets and Ajax?
Judging by your question, you're really not using a web framework but using servlets to render html.
I'll be nice and say that you're about a decade behind the times :), people use JSPs (and a web framework like struts) for this sort of thing. However, having said that, here goes:
Create a hidden field in your form and set the value to '1', '2' or '3' depending on which drop down is to be populated;
In your servlet, capture this value (request.getParamter()) and use it a 'case'/if/else statement to return the appropriate dropdown values.
I'll say it again, just use a web-framework, or atleast plain old jsp to do this.
You may need multiple servlets for this.
Servlet 1: Load the values for the first drop down list from the database. On the JSP page construct the drop down list. On the user selecting a value submit to servlet two.
Servlet 2: retrieve the value from the first list and perform your database search for the values of the second list. Construct the second list. When the user selects the second value submit it to servlet 3.
Servlet 3: retrieve the value selected in the second drop down and perform the database search to get values for the last drop down.
You may want to consider AJAX to make the populating of the lists appear seamless to the users. jQuery has some very nice plugins for making this quite easy if you are willing to do that.
<form action="servlet2.do">
<select name="dd1" onchange="Your JavaScript Here">
<option>....
</select>
</form>
You can write JavaScript that submits the form in the onchange event. Again, If you use an existing library like jQuery it will be 10 times simpler.
That was an awesome simple solution. I like how small the JQuery code is and really appreciate the link to the GSON API. All the examples made this an easy implementation.
Had one issue on building the JSON server URL with the reference to the parent SELECT ( e.g. $(this).val() ) [needed to specify the :selected attribute]. I've modified the script a little to include the suggested updates. Thanks for the initial code.
<script>
$(document).ready(function()
{
$('#dd1').change(function() { fillOptions('dd1', 'dd2'); });
$('#dd2').change(function() { fillOptions('dd2', 'dd3'); });
});
function fillOptions(parentId, ddId)
{
var dd = $('#' + ddId);
var jsonURL = 'json/options?dd=' + ddId + '&val=' + $('#' + parentId + ' :selected').val();
$.getJSON(jsonURL, function(opts)
{
$('>option', dd).remove(); // Clean old options first.
if (opts)
{
$.each(opts, function(key, value)
{
dd.append($('<option/>').val(key).text(value));
});
}
else
{
dd.append($('<option/>').text("Please select parent"));
}
});
}
</script>

Can an `input` of type `number` be made untypeable? [duplicate]

I have an input:
<input type="number" name="amount" min="4" max="6" step="2" value="4" id="amount" />
And I don't have a normal submit button to submit the info (uses javascript).
type=number works fine, but users can still type in any number they want, I would like to stop users from being able to type in the input, but still allow for changes using the "up/down" arrows that appear with type=number. I have tried researching but cannot find anything. Is this even possible?
Is there any reason you are avoiding using a drop down select tag?
This will give the user a very limited choice of numbers (set to your preference).
You could even populate the <option> fields with numbers 1 through 100 (or whatever you choose) using PHP or JavaScript so you didn't have to manually type each number in the HTML code.
<select>
<option value='1'>1</option>
<option value='2'>2</option>
<option value='3'>3</option>
</select>
Edited to block copy/paste:
<script type="text/javascript">
document.getElementById('amount').onkeypress = function(e) { e.preventDefault(); };
document.getElementById('amount').onkeydown = function(e) {
if(e.keyCode != 38 && e.keyCode != 40)
e.preventDefault();
};
if(document.addEventListener)
document.getElementById('amount').addEventListener('contextmenu',function(e) { e.preventDefault();
},false);
</script>
http://jsfiddle.net/Wh5Ms/2/
Then you could add a <noscript> tag for users with JavaScript turned off and show them a <select> element, etc.
The user is expected to be able to type in the field, as one option. Although it is possible to prevent this in part (namely when JavaScript is enabled and event handlers in your code cover the ways that the user might use), there is no point in using the element when you specifically do not want to get its basic functionality.
If you only want to allow the two values 4 and 6, as it seems from your example, and you want to prevent the user from simply typing one of them, then you should use a select element or a set of two radio buttons.
Although I am not sure as to what the step attribute does but pretty sure that you can find a way to use it as per your requirements in below code
The Code below makes sure that the allowed values are between min and max attribute specified only
The Code is using jquery 1.10 onwards.
var prevVal = 0;
$("input[type=number]").on("keydown", function(e){
prevVal = Number($(this).val());
});
$("input[type=number]").on("keyup", function(e){
var minVal = $(this).attr("min");
var maxVal = $(this).attr("max");
var step= $(this).attr("step");
var currentVal = $(this).val();
if(!(currentVal<=maxVal&&currentVal>=minVal)){
$(this).val(prevVal)
}
});
Try it dude, my problem was solved
var prevVal = 0;
$("input[type=number]").on("keydown", function(e){
prevVal = Number($(this).val());
});
$("input[type=number]").on("keyup", function(e){
var minVal = $(this).attr("min");
var maxVal = $(this).attr("max");
var step= $(this).attr("step");
var currentVal = $(this).val();
if(!(currentVal>=1)){
$(this).val(1)
}
});

HTML nav menu/form list that redirects on button click

Here is what I'm trying to do, and I feel as though maybe I'm overthinking it. I can easily make a dropdown nav list, but what I need is to combine two different dropdown lists to give a user options, then when they click the button it will send them to a page that corresponds with their choices.
For example, dropdown list one: What type of advice do you need?
-Career
-Relationships
-Health
droplist two: What layout do you want?
-Quick and Dirty
-Standard
-In-depth
Then I just need to program in the link to every combination of these pages. How do program in the correct redirects depending on the selections?
Assuming you have a pair of drop-down menus like this:
<form method="post">
<p>What type of advice do you need?
<br><select id="s1">
<option>Career</option>
<option>Relationships</option>
<option>Health</option>
</select>
<p>What layout do you want?
<br><select id="s2">
<option>Quick and Dirty</option>
<option>Standard</option>
<option>In-depth</option>
</select>
<input type="button" onclick="send(this.form)" value="Send"/>
</form>
The button at the end of the form, when clicked calls a function that will redirect to the page you want comparing the items selected in each menu. The function should receive a reference to the form (passed in this.form), get the selected indexes and text (or values, if you use them in each option) and test them in a condicional branch:
function send(form) {
var s1 = document.getElementById("s1");
var s2 = document.getElementById("s2");
var choice1 = s1.options[s1.selectedIndex].text;
var choice2 = s2.options[s2.selectedIndex].text;
if (choice1 == "Career" && choice2 == "Quick and Dirty") {
location.href = "http://quickdirtycareers.com";
} else if (choice1 == "Career" && choice2 == "Standard") {
location.href = "http://standardcareers.com";
} else {
location.href = "http://careers.stackoverflow.com";
}
}
Here's a Fiddle with a working sample: http://jsfiddle.net/helderdarocha/TNZ9A/
(There are many other ways to do it, adding CSS, dynamic selection, focus, etc. It's easier to add these enhancements using JQuery.)

HTML Select with disabled Option returns wrong selectedIndex in FireFox

I have a Select with a disabled Option wich is the default selected one:
<select name="select" size="1">
<option>0</option>
<option selected disabled>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
If I get the selected, it returns 1. Everything OK.
But if I open the popup and hover with the cursor over another Option (for Example '4') and Cancel it via ESC or by clicking anywhere else.
The Select input shows the old value 1 but returns on get selected 4.
Example with jsfiddle
It doesn't happen with Chrome only FireFox (4/5)
It appears that the display is not changed when you exit your select this way however firefox is looking for a different selectedValue because it finds the currently selected option as disabled, which in firefox' eyes should be impossible.
The onChange event was not triggered until the onBlur event (which is when the selectedValue would get changed, but this is not what the display is changed to). If we were to reset our value in the onChange event this event might get called again. So by utilising the onBlur event we can provide the following workaround:
onBlur="javascript:document.getElementsByName('select')[0].selectedIndex = document.getElementsByName('select')[0].selectedIndex;"
http://jsfiddle.net/aRMpt/22/
I hope I'm making sense here.
The following code is ugly, but it does exactly what you want (I think). Basically, I am intercepting all onChange events, and only processing them if there is a corresponding onClick event that results in a changed value. ALso note that change events are processed before click events. EDIT: Just relaized this does not work in chrome, so i added some browser detection code so that it only executes in firefox.
NOTE: The current code would not work if the user had tabbed into the select box and made his changes with the error keys, but the method below can be easily adapted to handle that case as well. You'd simply need to process key events like arrow up or arrow down or TAB or ENTER in the same way clicks are processed below, but only when the select box had focus.
NOTE 2: Playing with this more, the behavior is very strange. If you escape out of the select, the onChange event is not triggered, but it is saved up. If at any later time you click anywhere on the screen the onChange event will be triggered for the value you were hovering over when you escaped, even though that value was actually changed as soon as you escaped. So this is getting tricky. I think you may have to handle the 2 cases separately. One case to handle click aways, and one to handle escape outs (which patrick answered).
It's getting hairy and I see no elegant way to code this. How about a note to the user next to the text box saying "Your currently selected option, 1, is no longer available." Then you could have a select box with only the avalable options.
<select name="select" size="1" onChange="handleChange()" onClick="handleClick()" >
<option>0</option>
<option selected disabled>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
<br />
<script>
var initialValue = document.getElementsByName('select')[0].selectedIndex;
var potentialChange;
var processClick;
function handleClick() {
//ignore this code if not firefox
if (navigator.userAgent.indexOf("Firefox") === -1)
return;
var curVal = document.getElementsByName('select')[0].selectedIndex;
if (!processClick)
return;
// a value change click occured, now we actually process it.
document.getElementsByName('select')[0].value = potentialChange;
}
function handleChange() {
// save the potential change, which will be used if a real click was detected
potentialChange = document.getElementsByName('select')[0].selectedIndex;
processClick = (potentialChange !== initialValue);
// undo the attempted change, in case of an escape or page click
// but only on firefox
if (navigator.userAgent.indexOf("Firefox")!=-1)
document.getElementsByName('select')[0].value = initialValue;
document.getElementsByName('select')[0].value = initialValue;
}
</script>
getSelected
Detect the esc key and reset it, here is an example using jQuery (and a dash of your code)
$('select').keyup(function(e) {
if (e.keyCode == 27) {
document.getElementsByName('select')[0].selectedIndex = 1;
}
});
UPDATE No jQuery Solution
UPDATE 2 Abstracted out finding event and keycode for re-usability.
DEMO: http://wecodesign.com/demos/stackoverflow-6923135.htm
<script type="text/javascript">
function getEvent( event ) {
if ( window.event ) return window.event;
return event;
}
function getKeycode ( event ) {
if ( event.which ) return event.which;
else return event.keyCode;
}
changeToDefaultListener = function( event ) {
theEvent = getEvent( event );
theKeyCode = getKeycode( theEvent );
if( theKeyCode == 27 ) {
document.getElementsByName( 'select' )[0].selectedIndex = 1;
}
};
</script>
<select name="select" size="1">
<option>0</option>
<option selected disabled>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
getSelected
<script type="text/javascript">
document.getElementsByName('select')[0].onkeyup=changeToDefaultListener;
</script>
The work around I had to use for this is was putting an event listener on the click event of the not selected options in drop down. In the event listener's function I set a global boolean.
var selectChanged = false;
$('#select option:not(:selected)').click(function () {
selectChanged = true;
});
Then in the area of the code where I need the selected value of the drop down, I check the boolean:
if the boolean is true I can then use it's new value
if the boolean is false I can get the value from the Html property (which contains the initial value of the dropdown).
var selectValue = selectChanged ? $('#select').val() : $($('#select').outerHtml()).find('option:selected').val()
This is ugly I know, but this is the only work around I could find.

Tri-state Check box in HTML?

There is no way to have a tri-state check button (yes, no, null) in HTML, right?
Are there any simple tricks or work-arounds without having to render the whole thing by oneself?
Edit — Thanks to Janus Troelsen's comment, I found a better solution:
HTML5 defines a property for checkboxes called indeterminate
See w3c reference guide. To make checkbox appear visually indeterminate set it to true:
element.indeterminate = true;
Here is Janus Troelsen's fiddle. Note, however, that:
The indeterminate state cannot be set in the HTML markup, it can only be done via Javascript (see this JSfiddle test and this detailed article in CSS tricks)
This state doesn't change the value of the checkbox, it is only a visual cue that masks the input's real state.
Browser test: Worked for me in Chrome 22, Firefox 15, Opera 12 and back to IE7. Regarding mobile browsers, Android 2.0 browser and Safari mobile on iOS 3.1 don't have support for it.
Previous answer
Another alternative would be to play with the checkbox transparency
for the "some selected" state (as Gmail does used to
do in previous versions). It will require some javascript and a CSS
class. Here I put a particular example that handles a list with
checkable items and a checkbox that allows to select all/none of them.
This checkbox shows a "some selected" state when some of the list
items are selected.
Given a checkbox with an ID #select_all and several checkboxes with
a class .select_one,
The CSS class that fades the "select all" checkbox would be the
following:
.some_selected {
opacity: 0.5;
filter: alpha(opacity=50);
}
And the JS code that handles the tri-state of the select all checkbox
is the following:
$('#select_all').change (function ()
{
//Check/uncheck all the list's checkboxes
$('.select_one').attr('checked', $(this).is(':checked'));
//Remove the faded state
$(this).removeClass('some_selected');
});
$('.select_one').change (function ()
{
if ($('.select_one:checked').length == 0)
$('#select_all').removeClass('some_selected').attr('checked', false);
else if ($('.select_one:not(:checked)').length == 0)
$('#select_all').removeClass('some_selected').attr('checked', true);
else
$('#select_all').addClass('some_selected').attr('checked', true);
});
You can try it here: http://jsfiddle.net/98BMK/
You could use HTML's indeterminate IDL attribute on input elements.
My proposal would be using
three appropriate unicode characters for the three states e.g. ❓,✅,❌
a plain text input field (size=1)
no border
read only
display no cursor
onclick handler to toggle thru the three states
See examples at:
http://jsfiddle.net/wf_bitplan_com/941std72/8/
/**
* loops thru the given 3 values for the given control
*/
function tristate(control, value1, value2, value3) {
switch (control.value.charAt(0)) {
case value1:
control.value = value2;
break;
case value2:
control.value = value3;
break;
case value3:
control.value = value1;
break;
default:
// display the current value if it's unexpected
alert(control.value);
}
}
function tristate_Marks(control) {
tristate(control,'\u2753', '\u2705', '\u274C');
}
function tristate_Circles(control) {
tristate(control,'\u25EF', '\u25CE', '\u25C9');
}
function tristate_Ballot(control) {
tristate(control,'\u2610', '\u2611', '\u2612');
}
function tristate_Check(control) {
tristate(control,'\u25A1', '\u2754', '\u2714');
}
<input type='text'
style='border: none;'
onfocus='this.blur()'
readonly='true'
size='1'
value='❓' onclick='tristate_Marks(this)' />
<input style="border: none;"
id="tristate"
type="text"
readonly="true"
size="1"
value="❓"
onclick="switch(this.form.tristate.value.charAt(0)) {
case '&#x2753': this.form.tristate.value='✅'; break;
case '&#x2705': this.form.tristate.value='❌'; break;
case '&#x274C': this.form.tristate.value='❓'; break;
};" />
You can use radio groups to achieve that functionality:
<input type="radio" name="choice" value="yes" />Yes
<input type="radio" name="choice" value="No" />No
<input type="radio" name="choice" value="null" />null
Here is a runnable example using the mentioned indeterminate attribute:
const indeterminates = document.getElementsByClassName('indeterminate');
indeterminates['0'].indeterminate = true;
<form>
<div>
<input type="checkbox" checked="checked" />True
</div>
<div>
<input type="checkbox" />False
</div>
<div>
<input type="checkbox" class="indeterminate" />Indeterminate
</div>
</form>
Just run the code snippet to see how it looks like.
You can use an indeterminate state: http://css-tricks.com/indeterminate-checkboxes/. It's supported by the browsers out of the box and don't require any external js libraries.
I think that the most semantic way is using readonly attribute that checkbox inputs can have. No css, no images, etc; a built-in HTML property!
See Fiddle:
http://jsfiddle.net/chriscoyier/mGg85/2/
As described here in last trick:
http://css-tricks.com/indeterminate-checkboxes/
Like #Franz answer you can also do it with a select. For example:
<select>
<option></option>
<option value="Yes">Yes</option>
<option value="No">No</option>
</select>
With this you can also give a concrete value that will be send with the form, I think that with javascript indeterminate version of checkbox, it will send the underline value of the checkbox.
At least, you can use it as a callback when javascript is disabled. For example, give it an id and in the load event change it to the javascript version of the checkbox with indeterminate status.
Besides all cited above, there are jQuery plugins that may help too:
for individual checkboxes:
jQuery-Tristate-Checkbox-plugin: http://vanderlee.github.io/tristate/
for tree-like behavior checkboxes:
jQuery Tristate: http://jlbruno.github.io/jQuery-Tristate-Checkbox-plugin/
EDIT
Both libraries uses the 'indeterminate' checkbox attribute, since this attribute in Html5 is just for styling (https://www.w3.org/TR/2011/WD-html5-20110113/number-state.html#checkbox-state), the null value is never sent to the server (checkboxes can only have two values).
To be able to submit this value to the server, I've create hidden counterpart fields which are populated on form submission using some javascript. On the server side, you'd need to check those counterpart fields instead of original checkboxes, of course.
I've used the first library (standalone checkboxes) where it's important to:
Initialize the checked, unchecked, indeterminate values
use .val() function to get the actual value
Cannot make work .state (probably my mistake)
Hope that helps.
Refering to #BoltClock answer, here is my solution for a more complex recursive method:
http://jsfiddle.net/gx7so2tq/2/
It might not be the most pretty solution but it works fine for me and is quite flexible.
I use two data objects defining the container:
data-select-all="chapter1"
and the elements itself:
data-select-some="chapter1"
Both having the same value. The combination of both data-objects within one checkbox allows sublevels, which are scanned recursively. Therefore two "helper" functions are needed to prevent the change-trigger.
Here other Example with simple jQuery and property data-checked:
$("#checkbox")
.click(function(e) {
var el = $(this);
switch (el.data('checked')) {
// unchecked, going indeterminate
case 0:
el.data('checked', 1);
el.prop('indeterminate', true);
break;
// indeterminate, going checked
case 1:
el.data('checked', 2);
el.prop('indeterminate', false);
el.prop('checked', true);
break;
// checked, going unchecked
default:
el.data('checked', 0);
el.prop('indeterminate', false);
el.prop('checked', false);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<label><input type="checkbox" name="checkbox" value="" checked id="checkbox"> Tri-State Checkbox </label>
As I needed something like this -without any plug-in- for script-generated checkboxes in a table... I ended up with this solution:
<!DOCTYPE html>
<html>
<body>
Toto <input type="checkbox" id="myCheck1" onclick="updateChkBx(this)" /><br />
Tutu <input type="checkbox" id="myCheck2" onclick="updateChkBx(this)" /><br />
Tata <input type="checkbox" id="myCheck3" onclick="updateChkBx(this)" /><br />
Tete <input type="checkbox" id="myCheck4" onclick="updateChkBx(this)" /><br />
<script>
var chkBoxState = [];
function updateChkBx(src) {
var idx = Number(src.id.substring(7)); // 7 to bypass the "myCheck" part in each checkbox id
if(typeof chkBoxState[idx] == "undefined") chkBoxState[idx] = false; // make sure we can use stored state at first call
// the problem comes from a click on a checkbox both toggles checked attribute and turns inderminate attribute to false
if(chkBoxState[idx]) {
src.indeterminate = false;
src.checked = false;
chkBoxState[idx] = false;
}
else if (!src.checked) { // passing from checked to unchecked
src.indeterminate = true;
src.checked = true; // force considering we are in a checked state
chkBoxState[idx] = true;
}
}
// to know box state, just test indeterminate, and if not indeterminate, test checked
</script>
</body>
</html>
A short snippet using an auxiliary variable and indeterminate:
cb1.state = 1
function toggle_tristate(cb) {
cb.state = ++cb.state % 3 // cycle through 0,1,2
if (cb.state == 0) {
cb.indeterminate = true;
cb.checked = true; // after 'indeterminate' the state 'false' follows
}
}
<input id="cb1" type="checkbox" onclick="toggle_tristate(this)">
Only state==0 is captured. The rest is handle automatically.
http://jsfiddle.net/6vyek2c5
You'll need to use javascript/css to fake it.
Try here for an example: http://www.dynamicdrive.com/forums/archive/index.php/t-26322.html
It's possible to have HTML form elements disabled -- wouldn't that do? Your users would see it in one of three states, i.e. checked, unchecked, and disabled, which would be greyed out and not clickable. To me, that seems similar to "null" or "not applicable" or whatever you're looking for in that third state.
There's a simple JavaScript tri-state input field implementation at
https://github.com/supernifty/tristate-checkbox
The jQuery plugin "jstree" with the checkbox plugin can do this.
http://www.jstree.com/documentation/checkbox
-Matt
Building on the answers above using the indeterminate state, I've come up with a little bit that handles individual checkboxes and makes them tri-state.
MVC razor uses 2 inputs per checkbox anyway (the checkbox and a hidden with the same name to always force a value in the submit). MVC uses things like "true" as the checkbox value and "false" as the hidden of the same name; makes it amenable to boolean use in API calls. This snippet uses a third hidden state to persist the last request values across submits.
Checkboxes initialized with the below will start indeterminate. Checking once turns on the checkbox. Checking twice turns off the checkbox (returning the hidden value of the same name). Checking a third time returns it to indeterminate (and clears out the hidden so a submit will produce a blank).
The page also populates another hidden (e.g., triBox2Orig) with whatever value was on the query string to start, so the 3 states can be initialized and persisted between submits.
$(document).ready(function () {
var initCheckbox = function (chkBox)
{
var hidden = $('[name="' + $(chkBox).prop("name") + '"][type="hidden"]');
var hiddenOrig = $('[name="' + $(chkBox).prop("name") + 'Orig"][type="hidden"]').prop("value");
hidden.prop("origValue", hidden.prop("value"));
if (!chkBox.prop("checked") && !hiddenOrig) chkBox.prop("indeterminate", true);
if (chkBox.prop("indeterminate")) hidden.prop("value", null);
chkBox.change(checkBoxToggleFun);
}
var checkBoxToggleFun = function ()
{
var isChecked = $(this).prop('checked');
var hidden = $('[name="' + $(this).prop("name") + '"][type="hidden"]');
var thirdState = isChecked && hidden.prop("value") === hidden.prop("origValue");
if (thirdState) { // on 3rd click of a checkbox, set it back to indeterminate
$(this).prop("indeterminate", true);
$(this).prop('checked', false);
}
hidden.prop("value", thirdState ? null : hidden.prop("origValue"));
};
var chkBox = $('#triBox1');
initCheckbox(chkBox);
chkBox = $('#triBox2');
initCheckbox(chkBox);
});