I am trying to use the demo kendo ui dropdownlist. For some reason when I create my variable "dropdownlist" to set its datasource, the variable is null Can anyone see what needs to be changed?
Here is the demo of it being used http://demos.telerik.com/kendo-ui/dropdownlist/index
function getcombo() {
var data = getVillages();
console.log(data);
var dropdownlist = $("#color").data("kendoDropDownList");
console.log(dropdownlist); //error here
dropdownlist.setDataSource(data);
};
In my html I have:
<div id="example">
<div id="cap-view" class="demo-section k-header">
<h2>Customize your Kendo Cap</h2>
<div id="cap" class="black-cap"></div>
<div id="options">
<h3>Cap Color</h3>
<input id="color" value="1" />
<h3>Cap Size</h3>
<select id="size">
<option>S - 6 3/4"</option>
<option>M - 7 1/4"</option>
<option>L - 7 1/8"</option>
<option>XL - 7 5/8"</option>
</select>
<button class="k-button" id="get">Customize</button>
</div>
</div>
<style scoped>
.demo-section {
width: 460px;
height: 300px;
}
.demo-section h2 {
text-transform: uppercase;
font-size: 1em;
margin-bottom: 30px;
}
#cap {
float: left;
width: 242px;
height: 225px;
margin: 20px 30px 30px 0;
background-image: url('../content/web/dropdownlist/cap.png');
background-repeat: no-repeat;
background-color: transparent;
}
.black-cap {
background-position: 0 0;
}
.grey-cap {
background-position: 0 -225px;
}
.orange-cap {
background-position: 0 -450px;
}
#options {
padding: 1px 0 30px 30px;
}
#options h3 {
font-size: 1em;
font-weight: bold;
margin: 25px 0 8px 0;
}
#get {
margin-top: 25px;
}
</style>
<script>
$(document).ready(function() {
var data = [
{ text: "Black", value: "1" },
{ text: "Orange", value: "2" },
{ text: "Grey", value: "3" }
];
// create DropDownList from input HTML element
$("#color").kendoDropDownList({
dataTextField: "text",
dataValueField: "value",
dataSource: data,
index: 0,
change: onChange
});
// create DropDownList from select HTML element
$("#size").kendoDropDownList();
var color = $("#color").data("kendoDropDownList");
color.select(0);
var size = $("#size").data("kendoDropDownList");
function onChange() {
var value = $("#color").val();
$("#cap")
.toggleClass("black-cap", value == 1)
.toggleClass("orange-cap", value == 2)
.toggleClass("grey-cap", value == 3);
};
$("#get").click(function() {
alert('Thank you! Your Choice is:\n\nColor ID: '+color.value()+' and Size: '+size.value());
});
});
</script>
</div>
my guess would be "jQuery.data( element, key, value )"
normally 'null' only exists if the user sets the variable to explicitly be 'null' otherwise it would be undefined
by the way, you can console.log(arg1, arg2, etc.) with commas to log them all at once on the same line
Related
I am building a quiz app with Vue 3 and Bootstrap 4.
I have this method for checking if the clicked answer is the (same as the) correct answer:
checkAnswer(answer) {
return answer == this.results[this.questionCount]["correct_answer"];
}
It should be executed upon clicking a list item, as seen below:
<li v-for="answer in answers" #click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
If the clicked answer is correct, the list item should be added the classes text-white bg-success, otherwise it should be added text-white bg-danger.
const quizApp = {
data() {
return {
questionCount: 0,
results: [
{
question: "The book "The Little Prince" was written by...",
correct_answer: "Antoine de Saint-Exupéry",
incorrect_answers: [
"Miguel de Cervantes Saavedra",
"Jane Austen",
"F. Scott Fitzgerald"
]
},
{
question:
"Which novel by John Grisham was conceived on a road trip to Florida while thinking about stolen books with his wife?",
correct_answer: "Camino Island",
incorrect_answers: ["Rogue Lawyer", "Gray Mountain", "The Litigators"]
},
{
question:
"In Terry Pratchett's Discworld novel 'Wyrd Sisters', which of these are not one of the three main witches?",
correct_answer: "Winny Hathersham",
incorrect_answers: [
"Granny Weatherwax",
"Nanny Ogg",
"Magrat Garlick"
]
}
]
};
},
methods: {
nextQuestion() {
if (this.questionCount < this.results.length - 1) {
this.questionCount++;
}
},
prevQuestion() {
if (this.questionCount >= 1) {
this.questionCount--;
}
},
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
return answer == this.results[this.questionCount]["correct_answer"];
},
shuffle(arr) {
var len = arr.length;
var d = len;
var array = [];
var k, i;
for (i = 0; i < d; i++) {
k = Math.floor(Math.random() * len);
array.push(arr[k]);
arr.splice(k, 1);
len = arr.length;
}
for (i = 0; i < d; i++) {
arr[i] = array[i];
}
return arr;
}
},
computed: {
answers() {
let incorrectAnswers = this.results[this.questionCount][
"incorrect_answers"
];
let correctAnswer = this.results[this.questionCount]["correct_answer"];
// return all answers, shuffled
return this.shuffle(incorrectAnswers.concat(correctAnswer));
}
}
};
Vue.createApp(quizApp).mount("#quiz_app");
#quiz_app {
height: 100vh;
}
.container {
flex: 1;
}
.quetions .card-header {
padding-top: 1.25rem;
padding-bottom: 1.25rem;
}
.quetions .card-footer {
padding-top: 0.7rem;
padding-bottom: 0.7rem;
}
.answers li {
cursor: pointer;
display: block;
padding: 7px 15px;
margin-bottom: 5px;
border-radius: 6px;
border: 1px solid rgba(0, 0, 0, 0.1);
background: #fff;
}
.answers li:last-child {
margin-bottom: 0;
}
.answers li:hover {
background: #fafafa;
}
.pager {
list-style-type: none;
margin: 0;
padding: 0;
display: flex;
justify-content: space-between;
}
.pager li > a {
display: inline-block;
padding: 5px 10px;
text-align: center;
width: 100px;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 999px;
text-decoration: none !important;
color: #fff;
}
.pager li > a.disabled {
pointer-events: none;
background-color: #9d9d9d !important;
}
.logo {
width: 30px;
}
.nav-item {
width: 100%;
}
.card {
width: 100%;
}
#media (min-width: 768px) {
.nav-item {
width: auto;
}
.card {
width: 67%;
}
}
#media (min-width: 992px) {
.card {
width: 50%;
}
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://unpkg.com/vue#next"></script>
<div id="quiz_app" class="container quetions d-flex align-items-center justify-content-center my-3">
<div v-if="results.length" class="card shadow-sm">
<div class="card-header bg-light h6">
{{results[questionCount]['question']}}
</div>
<div class="card-body">
<ul class="answers list-unstyled m-0">
<li v-for="answer in answers" #click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
</ul>
</div>
<div class="card-footer bg-white">
<ul class="pager">
<li>Previous</li>
<li class="d-flex align-items-center text-secondary font-weight-bold small">Question {{questionCount + 1}} of {{results.length}}</li>
<li>Next</li>
</ul>
</div>
</div>
</div>
The problem
To my surprise, the checkAnswer(answer) method is executed before (and in the absence of) any click.
Question
What am I doing wrong?
UPDATED
checkAnswer() is invoked immediately if used outside a handler.
maybe this will help, when checkAnswer() is called, store the selected answer selectedAnswer and check if answer is correct isCorrect, and use these 2 states to compare the looped answers.
<li
v-for="answer in answers"
:key="answer"
#click="checkAnswer(answer)"
:class="{
'text-white bg-success' : (selectedAnswer === answer && isCorrect),
'text-white bg-danger' : (selectedAnswer === answer && !isCorrect)
}"
>
{{answer}}
</li>
data() {
return {
isCorrect: false,
selectedAnswer: ''
...
}
},
methods: {
checkAnswer(answer) {
// check if the clicked anwser is equal to the correct answer
this.selectedAnswer = answer
if (answer == this.results[this.questionCount]["correct_answer"]) {
this.isCorrect = true
} else {
this.isCorrect = false
}
},
}
https://jsfiddle.net/renzivan15/fw10q5og/12/
This is executing it before the click:
:class="{'text-white bg-success' : checkAnswer(answer)}".
You'll need to keep the state in a variable for each answer and update it within the method.
And as a side node, it is recommended to use :key for looped elements.
The issue is that the checkAnswer is referenced in the html template so it will execute immediately on render. You might want to try instead setting the class to some computed property instead of the function.
<li v-for="answer in answers" #click="checkAnswer(answer)" :class="{'text-white bg-success' : checkAnswer(answer)}">{{answer}}</li>
I am making a simple search app in a React function component, the dropdown menu works, and everything looks fine, but when I try to handle the results being clicked, no click registers.
I have used JavaScript to change the the dropdown div's display prop (when the search input is in focus) to block from hidden, and changed the height of it as well with JS. I'm not sure if that has something to do with it.
The event handler in useEffect shows nothing, and the handleClick function also does nothing. Please, I have no idea.
useEffect(() => {
document.addEventListener('click', handleClick);
});
function handleClick(e) {
console.log('handleClick fired');
console.log(e.target);
}
function renderSearchA() {
return (
<div>
<h1>COMBATANT A</h1>
<input
autoFocus
id="searchA"
type="text"
ref={inputA}
onKeyUp={handleChangeA}
onFocus={showResultsA}
onBlur={hideResults}
placeholder={setPlaceHolder()}
/>
<div id="results-div" ref={resultsDivA} className="results-shown">
<ul>{resultsA()}</ul>
</div>
</div>
);
}
function showResultsA() {
if (resultsDivA.current != undefined) {
resultsDivA.current.style = 'display: block;';
resultsDivA.current.style = 'height: 0px;';
resultsDivA.current.style =
'box-shadow: 0 0 0 2pt rgba(255, 108, 35, 0.7)';
window.setTimeout(() => {
// resultsDiv.current.classList.remove('results');
resultsDivA.current.classList.add('results-shown');
}, 200);
}
}
function resultsMap(x) {
if (x === 'A' && searchResults != undefined) {
return searchResults.map((a, b) => {
console.log(a);
return (
<li key={a[2]}>
<button onClick="handleClick()" className="hero">
{a[0]}
</button>
</li>
);
});
}
}
return (
<SearchDiv>
<div className="search-a">
{renderSearchA()}
<HeroInfo name="a" id="heroA" />
</div>
<div className="search-b">
{renderSearchB()}
<HeroInfo name="b" id="heroB" className="heroB" />
</div>
</SearchDiv>
);
And the CSS looks like this:
#searchA {
/* input */
width: $ { width };
padding: 0.5rem;
border-radius: 8px 8px 8px 8px;
&:focus {
border-radius: 8px 8px 0 0;
box-shadow: 0 0 0 2pt rgba(255, 108, 35, 0.7);
}
}
#results-div {
box-shadow: 0 0 0 2pt rgba(255, 108, 35, 0.7);
}
.results {
/* div */
border-radius: 0 0 8px 8px;
height: 0px;
display: none;
width: 0px;
}
.results-shown {
height: auto;
margin: auto;
width: $ { width };
display: block;
/* display: block; */
}
I have an angular7 app in which i have a dropdown in which when i select any item from dropdown so dropdown closed. But i also close and open dropdown on input click like toggle. Like if i click on input then dropdown should open and when i again click on input so dropdown should close. Here i create a stackblitz you can see here live example of what i have
I have my component.html like this
records = [];
selectedName : string = '';
nameDisplayModel = '';
users = [
{name : 'Fahad', value :'fahad'},
{name : 'Saad', value :'saad'},
{name : 'Anus', value :'anus'},
{name : 'Hazik', value :'hazik'},
{name : 'Ahsan', value :'ahsan'},
{name : 'Sohaib', value :'sohaib'}
]
credentialsSearchFilter(event: any) {
const val = event.target.value.toLowerCase();
this.records = this.users.filter(function(d) {
return d.name.toLowerCase().indexOf(val) !== -1 || !val;
});
}
hideList(){
this.records = [];
}
getNameValue(row){
console.log('hello')
this.nameDisplayModel = row.name;
this.users.forEach(item=>{
if(item.name === row.name){
this.selectedName = row.value;
}
})
this.records = [];
console.log(this.selectedName)
}
p {
font-family: Lato;
}
.custom-list{
box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12);
}
.autocomplete {
position: relative;
}
.suggestion-list{
position: absolute;
top: 100%;
left: 0;
right: 0;
list-style-type: none;
padding: 0;
box-shadow: 0 2px 4px -1px rgba(0,0,0,0.2), 0 4px 5px 0 rgba(0,0,0,0.14), 0 1px 10px 0 rgba(0,0,0,0.12);
background-color:white;
}
li {
padding: 0.5em;
line-height: 30px;
}
.wrapper{
padding: 20px;
}
li:hover{
background-color:#a560f3;
cursor : pointer;
color:white;
}
<div class="wrapper">
<div class="row mt-3">
<div class="col-6">
<div class="autocomplete">
<input type="text" class="form-control" placeholder="text here" (keyup)="credentialsSearchFilter($event)" (blur)="hideList()" [(ngModel)]="nameDisplayModel">
<ul *ngIf="records.length > 0" class="suggestion-list mt-2">
<li *ngFor="let record of records" (mousedown)="getNameValue(record)">{{record.name}}</li>
</ul>
</div>
</div>
</div>
</div>
Working example of StackBlitz Link is, and ...
add click toggle () event in your input like this...
<input type="text" class="form-control" placeholder="text here" (keyup)="credentialsSearchFilter($event)" (blur)="hideList()" [(ngModel)]="nameDisplayModel" (click)="toggle($event)">
Then, in your toggle() is...
toggle(event: any){
this.toggleBool === false ? this.toggleBool=true: this.toggleBool=false;
this.toggleBool ? this.credentialsSearchFilter(event) :this.hideList() ;
}
and default toggleBool is set to false as property...
toggleBool =false;
I am making a javascript form that includes a question being answered with only the number 1-6 (multiple choice) when the user finishes the form, there will be a result showing a chart (ChartJS). I've made that, but I want to show the user below the chart as following. If statement 1 is 1 and If statement 2 is 3 and If statement 3 is 4 then show .... . Here is my code:
/*-----------------------------------------------------
REQUIRE
-------------------------------------------------------*/
var yo = require('yo-yo')
var csjs = require('csjs-inject')
var minixhr = require('minixhr')
var chart = require('chart.js')
/*-----------------------------------------------------
THEME
-------------------------------------------------------*/
var font = 'Montserrat'
var yellow = 'hsla(52,35%,63%,1)'
var white = 'hsla(120,24%,96%,1)'
var violet = 'hsla(329,25%,45%,1)'
var lightBrown = 'hsla(29,21%,67%,1)'
var darkBrown = 'hsla(13,19%,45%,1)'
/*-----------------------------------------------------------------------------
LOADING FONT
-----------------------------------------------------------------------------*/
var links = ['https://fonts.googleapis.com/css?family=Montserrat']
var font = yo`<link href=${links[0]} rel='stylesheet' type='text/css'>`
document.head.appendChild(font)
/*-----------------------------------------------------------------------------
LOADING DATA
-----------------------------------------------------------------------------*/
var questions = [
`
Statement #1:
The next social network I build,
will definitely be for animals.
`,
`
Statement #2:
I really like to do my hobby
`,
`
Statement #3:
My friends say, my middle name should be "Halo".
`,
`
Statement #4:
Rhoma Irama is definitely one of my
favourite artists
`,
`
Statement #5:
I think I could spend all day just
sleeping at my couch
`,
`
Statement #6:
I have a really strong desire to succeed
`
]
var i = 0
var question = questions[i]
var results = []
var answerOptions = [1,2,3,4,5,6]
/*-----------------------------------------------------------------------------
QUIZ
-----------------------------------------------------------------------------*/
function quizComponent () {
var css = csjs`
.quiz {
background-color: ${yellow};
text-align: center;
font-family: 'Montserrat';
padding-bottom: 200px;
}
.welcome {
font-size: 4em;
padding: 50px;
color: ${darkBrown}
}
.question {
font-size: 2em;
color: ${white};
padding: 40px;
margin: 0 5%;
}
.answers {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: 0 5%;
}
.answer {
background-color: ${violet};
padding: 15px;
margin: 5px;
border: 2px solid ${white};
border-radius: 30%;
}
.answer:hover {
background-color: ${lightBrown};
cursor: pointer;
}
.instruction {
color: ${violet};
font-size: 1em;
margin: 0 15%;
padding: 20px;
}
.results {
background-color: ${white};
text-align: center;
font-family: 'Montserrat', cursive;
padding-bottom: 200px;
}
.resultTitle{
font-size: 4em;
padding: 50px;
color: ${darkBrown}
}
.back {
display: flex;
justify-content: center;
}
.backImg {
height: 30px;
padding: 5px;
}
.backText {
color: ${white};
font-size: 25px;
}
.showChart {
font-size: 2em;
color: ${violet};
margin: 35px;
}
.showChart:hover {
color: ${yellow};
cursor: pointer;
}
.myChart {
width: 300px;
height: 300px;
}
`
function template () {
return yo`
<div class="${css.quiz}">
<div class="${css.welcome}">
IQ Test
</div>
<div class="${css.question}">
${question}
</div>
<div class="${css.answers}">
${answerOptions.map(x=>yo`<div class="${css.answer}" onclick=${nextQuestion(x)}>${x}</div>`)}
</div>
<div class="${css.instruction}">
Choose how strongly do you agree with the statement<br>
(1 - don't agree at all, 6 - completely agree)
</div>
<div class="${css.back}" onclick=${back}>
<img src="http://i.imgur.com/L6kXXEi.png" class="${css.backImg}">
<div class="${css.backText}">Back</div>
</div>
</div>
`
}
var element = template()
document.body.appendChild(element)
return element
function nextQuestion(id) {
return function () {
if (i < (questions.length-1)) {
results[i] = id
i = i+1
question = questions[i]
yo.update(element, template())
} else {
results[i] = id
sendData(results)
yo.update(element, seeResults(results))
}
}
}
function seeResults(data) {
var ctx = yo`<canvas class="${css.myChart}"></canvas>`
return yo`
<div class="${css.results}">
<div class="${css.resultTitle}">
Your Result
</div>
<div class="${css.showChart}" onclick=${function(){createChart(ctx, data)}}>
Click to see the chart
</div>
${ctx}
</div>
`
}
function back() {
if (i > 0) {
i = i-1
question = questions[i]
yo.update(element, template())
}
}
function sendData(results) {
var request = {
url : 'https://cobatest-964fd.firebaseio.com/results.json',
method : 'POST',
data : JSON.stringify(results)
}
minixhr(request)
}
function createChart(ctx, myData) {
minixhr('https://cobatest-964fd.firebaseio.com/results.json', responseHandler)
function responseHandler (data, response, xhr, header) {
var data = JSON.parse(data)
var keys = Object.keys(data)
var arrayOfAnswers = keys.map(x=>data[x])
var stats = arrayOfAnswers.reduce(function(currentResult,answer,i) {
var newResult=currentResult.map((x,count)=>(x*(i+1)+answer[count])/(i+2))
return newResult
}, myData)
var data = {
labels: [
"Caring", "Eager", "Pessimist",
"Hard-headed", "Lazy", "Ambitious"
],
datasets: [
{
label: "My score",
backgroundColor: "rgba(179,181,198,0.2)",
borderColor: "rgba(179,181,198,1)",
pointBackgroundColor: "rgba(179,181,198,1)",
pointBorderColor: "#fff",
pointHoverBackgroundColor: "#fff",
pointHoverBorderColor: "rgba(179,181,198,1)",
data: myData
},
]
}
var myChart = new Chart(ctx, {
type: 'radar',
data: data,
options: {
scale: {
scale: [1,2,3,4,5,6],
ticks: {
beginAtZero: true
}
}
}
})
}
}
}
quizComponent()
Please do help! thank you
Just like onclick is an event, onsubmit is one too.
From w3schools:
in HTML: <element onsubmit="myScript">
in JS: object.onsubmit = function(){myScript};
in JS with eventListener:object.addEventListener("submit", myScript);
You can check it out on w3 schools:
onsubmit event
onsubmit attribute
more
Hey guys, I'm looking for a nice tri-state checkbox controller in JS ?
Got anything to recommend me?
The states I'm looking for are
Checked
Unchecked
Indifferent (Was never checked/unchecked)
Use radio buttons.
<input type="radio" name="tristate" value="checked" />Checked
<input type="radio" name="tristate" value="unchecked" />Unchecked
If none if the radios are turned on, then you have your third "indifferent" or null state.
You may want to look into EXTJS.
They have a big community that often builds things like this and I'm sure if you googled for one it might come up. Actually here you go you may be able to make a few changes to this and get it to work how you want:
http://extjs.net/forum/showthread.php?t=28096
Hope this helps!
I developed this solution while working on a project. It uses the indeterminate state of checkboxes (an attribute that cannot be accessed / set from markup). In my example, I have just one level of nesting but it can be nested indefinitely to allow for groups and sub-groups to be un, all, or partially selected.
The basic structure revolves around manipulating the indeterminate attribute like this:
<input type="checkbox" value="HasFavoriteColor" name="FavoriteColor" id="myCheckBox" />
<input type="hidden" id="FavoriteColorState" name="FavoriteColorState" /><!-- potential values: 0, 1, -1 -->
<script type="text/javascript">
//using JQuery
$("#myCheckBox").prop("indeterminate", true);
//using pure javascript
document.getElementById("myCheckBox").setAttribute("indeterminate", true);
</script>
I used it for a select-all here in my example, but it could be used on just an individual checkbox. It's important to know that this DOES NOT communicate state in a post-back to the server. A checkbox being posted is still true / false so indeterminate only affects UI. If you need to post values back, you will have to tie the indeterminate state to some hidden field to persist the value.
For more on the indeterminate state, see the following resources:
http://css-tricks.com/indeterminate-checkboxes/
http://www.w3.org/TR/html-markup/input.checkbox.html
Here is a working example (external fiddle):
http://jsfiddle.net/xDaevax/65wZt/
Working Example (Stack Snippet):
var root = this;
root.selectedCount = 0;
root.totalCount = 0;
root.percentageSelected = 0.0;
root.holdTimer = 0;
jQuery.fn.customSelect = {
State: 0,
NextState: function () {
this.State += 1;
if (this.State > 2) {
this.State = 0;
} // end if
} // end object
};
function checkAllToggle(parent, toggle) {
if (parent != null && parent != undefined) {
parent.find("input[type='checkbox']").prop("checked", toggle);
for (var i = 0; i < parent.find("input[type='checkbox']").length; i++) {
$(document).trigger("item-selected", {
IsSelected: $(parent.find("input[type='checkbox']")[i]).prop("checked")
});
} // end for loop
} // end if
} // end function checkAll
var fadeTimer = setInterval(function () {
if (root.holdTimer > 0) {
root.holdTimer -= 1;
} else {
root.holdTimer = -2;
} // end if/else
if (root.holdTimer == -2) {
$(".options-status").fadeOut("easeOutBack");
root.holdTimer = -1;
} // end if/else
}, 50);
$(function () {
root.totalCount = $(document).find(".options-list input[type='checkbox']").length;
$(document).bind("select-state-change", function (e, data) {
switch (data.State) {
case 0:
data.Target.prop("checked", false);
data.Target.prop("indeterminate", false);
checkAllToggle($(".options-list"), false);
break;
case 1:
data.Target.prop("indeterminate", true);
e.preventDefault();
break;
case 2:
data.Target.prop("checked", true);
data.Target.prop("indeterminate", false);
checkAllToggle($(".options-list"), true);
break;
}
});
$(document).bind("item-selected", function (e, data) {
root.holdTimer = 50;
if (data != null && data != undefined) {
if (data.IsSelected) {
root.selectedCount += 1;
} else {
root.selectedCount -= 1;
} // end if/else
if (root.selectedCount > root.totalCount) {
root.selectedCount = root.totalCount;
} // end if
if (root.selectedCount < 0) {
root.selectedCount = 0;
} // end if
root.percentageSelected = (100 * (root.selectedCount / root.totalCount));
root.percentageSelected < 50 && root.percentageSelected >= 0 ? $(".options-status").removeClass("finally-there").removeClass("almost-there").addClass("not-there", 200) : false;
root.percentageSelected < 100 && root.percentageSelected >= 50 ? $(".options-status").removeClass("not-there").removeClass("finally-there").addClass("almost-there", 200) : false;
root.percentageSelected == 100 ? $(".options-status").removeClass("not-there").removeClass("almost-there").addClass("finally-there", 200) : false;
$(".options-status .output").text(root.percentageSelected + "%");
setTimeout(function () {
$(".options-status").fadeIn("easeInBack");
}, 100);
} // end if
});
$("#select-all").click(function (e) {
var checkbox = $(this);
if (checkbox.prop("checked") == true) {
checkbox.customSelect.State = 2;
} else {
checkbox.customSelect.State = 0;
} // end if/else
$(document).trigger("select-state-change", {
State: checkbox.customSelect.State,
Target: $("#select-all")
});
});
$("input[name='options']").each(function () {
$(this).click(function () {
if ($(this).prop("checked") == true) {
$(document).trigger("item-selected", {
IsSelected: true
});
if ($(this).parent().parent().find("input[type='checkbox']:checked").length == $(this).parent().parent().find("input[type='checkbox']").length) {
$(document).trigger("select-state-change", {
State: 2,
Target: $("#select-all")
});
} else {
$(document).trigger("select-state-change", {
State: 1,
Target: $("#select-all")
});
} // end if/else
} else {
$(document).trigger("item-selected", {
IsSelected: false
});
if ($(this).parent().parent().find("input[type='checkbox']:checked").length <= 0) {
$(document).trigger("select-state-change", {
State: 0,
Target: $("#select-all")
});
} else {
$(document).trigger("select-state-change", {
State: 1,
Target: $("#select-all")
});
} // end if/else
} // end if/else
});
});
});
body {
font-family: Helvetica, Verdana, Sans-Serif;
font-size: small;
color: #232323;
background-color: #efefef;
padding: 0px;
margin: 0px;
}
H1 {
margin-top: 2px;
text-align: center;
}
LEGEND {
margin-bottom: 6px;
}
.content-wrapper {
padding: 2px;
margin: 3px auto;
width: 100%;
max-width: 500px;
min-width: 250px;
}
.wrapper {
padding: 3px;
margin: 2px;
}
.container {
border-right: solid 1px #788967;
border-bottom: solid 1px #677867;
border-top: solid 1px #89ab89;
border-left: solid 1px #89ab89;
}
.rounded {
border-radius: 2px;
}
.contents {
background: linear-gradient(rgba(255, 255, 255, 1), rgba(180, 180, 180, .2));
}
.header {
padding: 4px;
border: solid 1px #000000;
background-color: rgba(200, 200, 230, .8);
font-size: 123%;
background-image: linear-gradient(rgba(220, 220, 255, .8), rgba(200, 200, 230, .8));
}
#options-chooser {
margin-top: 30px;
display: block;
}
#options-chooser .options-list > LABEL {
display: table-row;
height: 26px;
}
.red {
color: red;
}
.blue {
color: blue;
}
.green {
color: green;
}
.black {
color: black;
}
.options-status {
float: right;
right: 3%;
clear: both;
display: none;
margin-top: -20px;
}
.output {
font-weight: bold;
}
.not-there {
border-color: rgba(190, 190, 190, .3);
background-color: rgba(190, 190, 190, .1);
}
.almost-there {
border-color: rgba(220, 220, 50, .6);
background-color: rgba(220, 220, 50, .3);
}
.finally-there {
border-color: rgba(50, 190, 50, .3);
background-color: rgba(50, 190, 50, .1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.2/jquery-ui.js"></script>
<div class="content-wrapper">
<div class="wrapper container rounded">
<div class="contents">
<h1 class="header rounded">Partial Select All Example</h1>
<p>This example demonstrates how to implement a tri-state checkbox with options.</p>
<form name="options-chooser" id="options-chooser" method="post">
<fieldset class="rounded options">
<legend class="rounded header">Options
<input type="checkbox" value="options-all" name="selectAll" id="select-all" title="Select All" />
</legend> <span class="options-status rounded container wrapper">Items Selected: <span class="output"></span></span>
<div class="options-list">
<label class="blue">
<input type="checkbox" name="options" value="1" />Blue</label>
<label class="green">
<input type="checkbox" name="options" value="2" />Green</label>
<label class="red">
<input type="checkbox" name="options" value="3" />Red</label>
<label class="black">
<input type="checkbox" name="options" value="4" />Black</label>
</div>
</fieldset>
</form>
</div>
</div>
</div>
If you need more than two states, then use 3 radio buttons.
Don't assume if the user didn't select anything to mean the third state. What if the user missed the question all together, or hit submit by mistake?
If you want 3 states, then have 3 states!
Use HTML5 indeterminate input elements.
I have a solution using a native checkbox and the indeterminate property and storing a custom attribute in the checkbox to capture the current state to achieve an effective triple state checkbox.
This solution has tested out well on latest Chrome and Firefox and chromium (nw.js) on Linux and IE 11, Firefox and Chrome on Windohs.
I have posted this solution to JSFiddle.
Here is the utility singleton I used:
tscb$ = {
STATE_UNCHECKED: 0
,STATE_CHECKED: 1
,STATE_INDETER: 2
,setState: function(o,iState){
var t=this;
if(iState==t.STATE_UNCHECKED){
o.indeterminate=false; o.checked=false;
} else if(iState==t.STATE_CHECKED){
o.indeterminate=false; o.checked=true;
} else if(iState==t.STATE_INDETER){
o.checked=false; o.indeterminate=true;
} else {
throw new Error("Invalid state passed: `"+iState+"`")
}
o.setAttribute("tscbState", iState);
}
// Event to call when the cb is clicked to toggle setting.
,toggleOnClick: function(o){
var t=this, iNextState=t.getNextState(o)
if(iNextState==t.STATE_UNCHECKED){
o.checked=false;
} else if(iNextState==t.STATE_CHECKED){
o.checked=true;
} else if(iNextState==t.STATE_INDETER){
o.indeterminate=true;
}
o.setAttribute("tscbState", iNextState);
}
// For retrieval of next state
,getNextState: function(o){
var t=this, iState=t.getState(o)
if(iState==t.STATE_UNCHECKED){
return t.STATE_INDETER;
} else if(iState==t.STATE_CHECKED){
return t.STATE_UNCHECKED;
} else if(iState==t.STATE_INDETER){
return t.STATE_CHECKED;
}
}
,getState: function(o){
return parseInt(o.getAttribute("tscbState"))
}
}
Usage:
tscb$.setState() is used to initialize or override a setting for a checkbox
tscb$.toggleOnClick() is used when the element is clicked to toggle to the next state
tscb$.getState() is to retrieve the current state
tscb$.getNextState() is to retrieve the next state
Its amazing how effective a triple state checkbox can be at keeping a UI compact while allowing for unique filtering functionality. Its very effective for dynamic search result filtering where a filter can be True / False / Off with just one control