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
Related
I've made a menu that reveals a drop down menu when you click or touch it. At least that's what happens when you select the word 'Menu2' but unfortunately it's not what happens when you select the words 'Menu3'.
On Menu3, for some reason my code is not recognising the selection of the anchor element and then as a consequence the id of that anchor element is not being passed to the functions which will make the sub-menu appear and disappear.
The strangest thing is that when I replace the 'else if' statement with an 'if' statement the menu under 'Menu2' will appear when I select 'Menu3'!
The thing I took from this was that the querySelectorAll method and the For loop are working. It remains a mystery me why the third menu choice can't be selected.
My question is can anyone work why the menu below 'Menu3' is not opening and closing?
The listeners in the javascript code are activated when the window is loaded.
var timeout = 500;
var closetimer = 0;
var ddmenuitem = 0;
function listen(elem, evnt, func) {
if (elem.addEventListener) { //W3C DOMS.
elem.addEventListener(evnt, func, false);
} else if (elem.attachEvent) { //IE DOM 7
var r = elem.attachEvent("on" + evnt, func);
return r;
}
}
function attachListeners() {
var selectors = document.querySelectorAll("a#a2, a#a3");
for (var i = 0; i < selectors.length; i++) {
selectors[i].addEventListener("focus", function(event) {
var id_of_clicked_element = event.target.id
});
if (id_of_clicked_element = 'a2') {
var touch_div = document.getElementById(id_of_clicked_element);
// return false;
} else if (id_of_clicked_element = 'a3') {
touch_div = document.getElementById(id_of_clicked_element);
//return false;
}
}
listen(touch_div, 'touchstart', function(event) {
// get new layer and show it
event.preventDefault();
mopen(id_of_clicked_element);
}, false);
listen(touch_div, 'mouseover', function(event) {
// get new layer and show it
mopen(id_of_clicked_element);
}, false);
listen(touch_div, 'click', function(event) {
// get new layer and show it
mopen(id_of_clicked_element);
}, false);
}
function m1View() {
var y = document.getElementById('m1');
if (y.style.visibility === 'hidden') {
y.style.visibility = 'visible';
} else {
y.style.visibility = 'hidden';
}
}
function m2View() {
var z = document.getElementById('m2');
if (z.style.visibility === 'hidden') {
z.style.visibility = 'visible';
} else {
z.style.visibility = 'hidden';
}
}
// open hidden layer
function mopen(x) { // get new layer and show it
var openmenu = x;
if (openmenu = 'a2') {
m1View();
} else if (openmenu = 'a3') {
m2View();
}
}
window.onload = attachListeners;
#ddm {
margin: 0;
padding: 0;
z-index: 30
}
#ddm li {
margin: 0;
padding: 0;
list-style: none;
float: left;
font: bold 14px arial
}
#ddm li a {
display: block;
margin: 0 0 0 0;
padding: 12px 17px;
width: 130px;
background: #CC0066;
color: #FFF;
text-align: center;
text-decoration: none
}
#ddm li a:hover {
background: #CC0066
}
#ddm div {
position: absolute;
visibility: hidden;
margin: 0;
padding: 0;
background: #EAEBD8;
border: 1px solid #5970B2
}
#ddm div a {
position: relative;
display: block;
margin: 0;
padding: 5px 10px;
width: 130px;
white-space: nowrap;
text-align: left;
text-decoration: none;
background: #EAEBD8;
color: #5C124A;
font: 13px arial;
border: 1px solid #CC0066
}
#ddm div a:hover {
background: #CC0066;
color: #FFF
}
<ul id="ddm">
<li>Menu1</li>
<li>
Menu2
<div id="m1">
Dropdown 1.1
Dropdown 1.2
Dropdown 1.3
Dropdown 1.4
Dropdown 1.5
Dropdown 1.6
</div>
</li>
<li>
Menu3
<div id="m2">
Menu4
</div>
</li>
<li>Menu5</li>
<li>Menu6</li>
</ul>
<div style="clear:both"></div>
A JSfiddle can be found here: https://jsfiddle.net/Webfeet/z9x6Ly6k/27/
Thank you for any help anyone can provide.
NewWeb
I'd suggest a couple of things. First, like Leo Li suggested, I think you may have overcomplicated this a little. For instance, you could replace your attachListeners function with something like this:
function attachListeners() {
var selectors = document.querySelectorAll("a#a2, a#a3");
for (var i = 0; i < selectors.length; i++) {
selectors[i].addEventListener('touchstart', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
selectors[i].addEventListener('mouseover', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
selectors[i].addEventListener('click', function(event){
event.preventDefault();
mopen(event.target.id);
}, false);
}
}
But, besides that, one of the biggest problems is in the mopen() function. Instead of checking the value being passed in for x, you're reassigning it. Just switch the equals signs with triple equals, like this:
if (openmenu === 'a2') {
m1View();
} else if (openmenu === 'a3') {
m2View();
}
It's still probably not quite what you're looking for but here's a fork of your JSfiddle with my changes - https://jsfiddle.net/n90ryvfd/
Hope that helps!
i'm currently working through Udemy's Vue.js tutorial. I've reached the section where you are building a battle web app game. After finishing it, I decided to practice my refactoring and came across this bug.
When you click the attack buttons and then the confirm box comes up to ask if you want to play again, it seems to add one extra item in my log array instead of resetting the game fully.
I'm suspecting it is to do with pressing the attack buttons too quickly, and then the confirm box comes up before running an addToLog() and then it runs it afterwards.
Or it could be my bad code. lol
Note that I know that clicking cancel on the confirm box also comes up with bugs too.
index.html
<!DOCTYPE html>
<html>
<head>
<title>Monster Slayer</title>
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<link rel="stylesheet" href="css/foundation.min.css">
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<section class="row">
<div class="small-6 columns">
<h1 class="text-center">YOU</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: playerHealth + '%'}">
{{ playerHealth }}
</div>
</div>
</div>
<div class="small-6 columns">
<h1 class="text-center">BADDY</h1>
<div class="healthbar">
<div class="healthbar text-center" style="background-color: green; margin: 0; color: white;" :style="{width: computerHealth + '%'}">
{{ computerHealth }}
</div>
</div>
</div>
</section>
<section class="row controls" v-if="!isRunning">
<div class="small-12 columns">
<button id="start-game" #click="startGame">START GAME</button>
</div>
</section>
<section class="row controls" v-else>
<div class="small-12 columns">
<button id="attack" #click="attack">ATTACK</button>
<button id="special-attack" #click="specialAttack">SPECIAL ATTACK</button>
<button id="heal" #click="heal">HEAL</button>
<button id="restart" #click="restart">RESTART</button>
</div>
</section>
<section class="row log" v-if="turns.length > 0">
<div class="small-12 columns">
<ul>
<li v-for="turn in turns" :class="{'player-turn': turn.isPlayer, 'monster-turn': !turn.isPlayer}">
{{ turn.text }}
</li>
</ul>
</div>
</section>
</div>
<script src="app.js"></script>
</body>
</html>
css/app.css
.text-center {
text-align: center;
}
.healthbar {
width: 80%;
height: 40px;
background-color: #eee;
margin: auto;
transition: width 500ms;
}
.controls,
.log {
margin-top: 30px;
text-align: center;
padding: 10px;
border: 1px solid #ccc;
box-shadow: 0px 3px 6px #ccc;
}
.turn {
margin-top: 20px;
margin-bottom: 20px;
font-weight: bold;
font-size: 22px;
}
.log ul {
list-style: none;
font-weight: bold;
text-transform: uppercase;
}
.log ul li {
margin: 5px;
}
.log ul .player-turn {
color: blue;
background-color: #e4e8ff;
}
.log ul .monster-turn {
color: red;
background-color: #ffc0c1;
}
button {
font-size: 20px;
background-color: #eee;
padding: 12px;
box-shadow: 0 1px 1px black;
margin: 10px;
}
#start-game {
background-color: #aaffb0;
}
#start-game:hover {
background-color: #76ff7e;
}
#attack {
background-color: #ff7367;
}
#attack:hover {
background-color: #ff3f43;
}
#special-attack {
background-color: #ffaf4f;
}
#special-attack:hover {
background-color: #ff9a2b;
}
#heal {
background-color: #aaffb0;
}
#heal:hover {
background-color: #76ff7e;
}
#restart {
background-color: #ffffff;
}
#restart:hover {
background-color: #c7c7c7;
}
app.js
new Vue({
el: app,
data: {
playerHealth: 100,
computerHealth: 100,
isRunning: false,
turns: [],
},
methods: {
startGame: function() {
this.isRunning = true;
this.playerHealth = 100;
this.computerHealth = 100;
this.clearLog();
},
attackController: function(attacker, maxRange, minRange) {
let receiver = this.setReceiver(attacker);
let damage = 0;
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
if (attacker === 'computer') {
damage = this.randomDamage(maxRange, minRange);
this.playerHealth -= damage;
}
this.addToLog(attacker, receiver, damage);
if (this.checkWin()) {
return;
}
},
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
specialAttack: function() {
this.attackController('player', 30, 5);
this.attackController('computer', 30, 5);
},
heal: function() {
if (this.playerHealth <= 90) {
this.playerHealth += 10;
} else {
this.playerHealth = 100;
}
this.turns.unshift({
isPlayer: true,
text: 'Player heals for ' + 10,
});
},
randomDamage: function(max, min) {
return Math.floor(Math.random() * max, min);
},
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
}
return false;
},
alertBox: function(message) {
if (confirm(message)) {
this.isRunning = false;
this.startGame();
} else {
this.isRunning = false;
}
return true;
},
restart: function() {
this.isRunning = false;
this.startGame();
},
addToLog: function(attacker, receiver, damage) {
this.turns.unshift({
isPlayer: attacker === 'player',
text: attacker + ' hits ' + receiver + ' for ' + damage,
});
},
clearLog: function() {
this.turns = [];
},
setReceiver: function(attacker) {
if (attacker === 'player') {
return 'computer';
} else {
return 'player';
}
},
damageOutput: function(attacker, health) {
if (attacker === 'player') {
damage = this.randomDamage(maxRange, minRange);
this.computerHealth -= damage;
}
},
},
});
Github repo is here if you prefer that. Thanks!
Your attack (and specialAttack) function attacks for both players:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
},
Currently, it is checking for win at every attackController call. So when the first attacker (player) wins, the game resets AND the second player attacks.
So, my suggestion, move the checkWin out of the attackController into the attack functions:
attack: function() {
this.attackController('player', 10, 3);
this.attackController('computer', 10, 3);
this.checkWin();
},
The same to specialAttack.
Code/JSFiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/10/
Note, when the player wins, in the code above, the computer will still "strike back", even though the game is over. If you want to halt that, make checkWin return if the game is over:
checkWin: function() {
if (this.computerHealth <= 0) {
this.alertBox('YOU WIN! New Game?');
return true;
} else if (this.playerHealth <= 0) {
this.alertBox('LOSER!!! New Game?');
return true;
}
return false;
},
And add an if to attack (and specialAttack):
attack: function() {
this.attackController('player', 10, 3);
if (this.checkWin()) return;
this.attackController('computer', 10, 3);
this.checkWin();
},
Updated fiddle: https://jsfiddle.net/acdcjunior/wwc1xnyc/13/
I am working on a live validation function. The problem is that the aftermath of the conditions in the function executes executes before the last part is met. The entry validation indicator should not turn green until the conditions are met, however this is not the case.
The indicator turns green after the third condition is met. It should not, until all conditions are met. Any suggestions on how I can solve this problem.
My code looks like below.
$(function() {
// Pre-define extensions
var xTension = ".com .net .edu";
$("input").keyup(function() {
// Check the position of "#" symbol
var firstLetter = $(this).val().slice(0, 1);
var lastLetter = $(this).val().slice(-1);
var userXs = "No";
// User provided extension
var userX = $(this).val();
userX = userX.substr(userX.indexOf(".") + 0);
if (xTension.indexOf(userX) > -1) {
if (userX != "") {
userXs = "Yes";
} else {
userXs = "No";
}
} else {
userXs = "No";
};
if ($(this).val().indexOf("#") > -1 && (firstLetter != "#") && (lastLetter != "#") && (userXs != "No")) {
$("#input-status").removeClass("red").addClass("green");
} else {
$("#input-status").removeClass("green").addClass("red");
}
});
});
.rack {
width: 250px;
margin: 0 auto;
}
#input-status {
width: 1px;
height: 3px;
with: 0;
display: inline-block;
transition: all 2s;
}
input {
width: 230px;
height: 30px;
text-align: center;
}
#input-status.green,
#input-status.red {
width: 235px;
background: darkGreen;
transition: all 2s;
}
#input-status.red {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="rack">
<h1>Live Validat.ion</h1>
<input type="text" placeholder="Enter email address">
<div id="input-status">
</div>
</div>
Make xTension an array, not a string. If the user types user#foo.c, userX will be .c and this will be matched by indexOf() with the string, since there's nothing that forces it to match whole words. When you do this, you no longer need to check whether userX is an empty string.
I've made a few other simplifications to the code:
Instead of getting the first and last characters, just test the position of # against appropriate limits.
No need for + 0 after indexOf().
Don't keep calling $(this).val(), put it in a variable.
User a boolean variable for userXs.
$(function() {
// Pre-define extensions
var xTension = [".com", ".net", ".edu"];
$("input").keyup(function() {
var val = $(this).val();
// User provided extension
userX = val.substr(val.indexOf("."));
userXs = xTension.indexOf(userX) > -1;
atPos = val.indexOf("#");
if (atPos > 0 && atPos < val.length - 1 && userXs) {
$("#input-status").removeClass("red").addClass("green");
} else {
$("#input-status").removeClass("green").addClass("red");
}
});
});
.rack {
width: 250px;
margin: 0 auto;
}
#input-status {
width: 1px;
height: 3px;
with: 0;
display: inline-block;
transition: all 2s;
}
input {
width: 230px;
height: 30px;
text-align: center;
}
#input-status.green,
#input-status.red {
width: 235px;
background: darkGreen;
transition: all 2s;
}
#input-status.red {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="rack">
<h1>Live Validat.ion</h1>
<input type="text" placeholder="Enter email address">
<div id="input-status">
</div>
</div>
I am trying to show a description when hovering over an option in a select list, however, I am having trouble getting the code to recognize when hovering.
Relevant code:
Select chunk of form:
<select name="optionList" id="optionList" onclick="rankFeatures(false)" size="5"></select>
<select name="ranks" id="ranks" size="5"></select>
Manipulating selects (arrays defined earlier):
function rankFeatures(create) {
var $optionList = $("#optionList");
var $ranks = $("#ranks");
if(create == true) {
for(i=0; i<5; i++){
$optionList.append(features[i]);
};
}
else {
var index = $optionList.val();
$('#optionList option:selected').remove();
$ranks.append(features[index]);
};
}
This all works. It all falls apart when I try to deal with hovering over options:
$(document).ready(
function (event) {
$('select').hover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
I found that code while searching through Stack Exchange, yet I am having no luck getting it to work. The alert occurs when I click on an option. If I don't move the mouse and close the alert by hitting enter, it goes away. If I close out with the mouse a second alert window pops up. Just moving the mouse around the select occasionally results in an alert box popping up.
I have tried targeting the options directly, but have had little success with that. How do I get the alert to pop up if I hover over an option?
You can use the mouseenter event.
And you do not have to use all this code to check if the element is an option.
Just use the .on() syntax to delegate to the select element.
$(document).ready(function(event) {
$('select').on('mouseenter','option',function(e) {
alert('yeah');
// this refers to the option so you can do this.value if you need..
});
});
Demo at http://jsfiddle.net/AjfE8/
try with mouseover. Its working for me. Hover also working only when the focus comes out from the optionlist(like mouseout).
function (event) {
$('select').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
})
})
You don't need to rap in in a function, I could never get it to work this way. When taking it out works perfect. Also used mouseover because hover is ran when leaving the target.
$('option').mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
console.log('yeah!');
};
})
Fiddle to see it working. Changed it to console so you don't get spammed with alerts. http://jsfiddle.net/HMDqb/
That you want is to detect hover event on option element, not on select:
$(document).ready(
function (event) {
$('#optionList option').hover(function(e) {
console.log(e.target);
});
})
I have the same issue, but none of the solutions are working.
$("select").on('mouseenter','option',function(e) {
$("#show-me").show();
});
$("select").on('mouseleave','option',function(e) {
$("#show-me").hide();
});
$("option").mouseover(function(e) {
var $target = $(e.target);
if($target.is('option')) {
alert('yeah!');
};
});
Here my jsfiddle https://jsfiddle.net/ajg99wsm/
I would recommend to go for a customized variant if you like to ease
capture hover events
change hover color
same behavior for "drop down" and "all items" view
plus you can have
resizeable list
individual switching between single selection and multiple selection mode
more individual css-ing
multiple lines for option items
Just have a look to the sample attached.
$(document).ready(function() {
$('.custopt').addClass('liunsel');
$(".custopt, .custcont").on("mouseover", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "block")
} else {
$(this).addClass("lihover");
}
})
$(".custopt, .custcont").on("mouseout", function(e) {
if ($(this).attr("id") == "crnk") {
$("#ranks").css("display", "none")
} else {
$(this).removeClass("lihover");
}
})
$(".custopt").on("click", function(e) {
$(".custopt").removeClass("lihover");
if ($("#btsm").val() == "ssm") {
//single select mode
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
$(this).removeClass("liunsel");
$(this).addClass("lisel");
} else if ($("#btsm").val() == "msm") {
//multiple select mode
if ($(this).is(".lisel")) {
$(this).addClass("liunsel");
$(this).removeClass("lisel");
} else {
$(this).addClass("lisel");
$(this).removeClass("liunsel");
}
}
updCustHead();
});
$(".custbtn").on("click", function() {
if ($(this).val() == "ssm") {
$(this).val("msm");
$(this).text("switch to single-select mode")
} else {
$(this).val("ssm");
$(this).text("switch to multi-select mode")
$(".custopt").removeClass("lisel");
$(".custopt").addClass("liunsel");
}
updCustHead();
});
function updCustHead() {
if ($("#btsm").val() == "ssm") {
if ($(".lisel").length <= 0) {
$("#hrnk").text("current selected option");
} else {
$("#hrnk").text($(".lisel").text());
}
} else {
var numopt = +$(".lisel").length,
allopt = $(".custopt").length;
$("#hrnk").text(numopt + " of " + allopt + " selected option" + (allopt > 1 || numopt === 0 ? 's' : ''));
}
}
});
body {
text-align: center;
}
.lisel {
background-color: yellow;
}
.liunsel {
background-color: lightgray;
}
.lihover {
background-color: coral;
}
.custopt {
margin: .2em 0 .2em 0;
padding: .1em .3em .1em .3em;
text-align: left;
font-size: .7em;
border-radius: .4em;
}
.custlist,
.custhead {
width: 100%;
text-align: left;
padding: .1em;
border: LightSeaGreen solid .2em;
border-radius: .4em;
height: 4em;
overflow-y: auto;
resize: vertical;
user-select: none;
}
.custlist {
display: none;
cursor: pointer;
}
.custhead {
resize: none;
height: 2.2em;
font-size: .7em;
padding: .1em .4em .1em .4em;
margin-bottom: -.2em;
width: 95%;
}
.custcont {
width: 7em;
padding: .5em 1em .6em .5em;
/* border: blue solid .2em; */
margin: 1em auto 1em auto;
}
.custbtn {
font-size: .7em;
width: 105%;
}
h3 {
margin: 1em 0 .5em .3em;
font-weight: bold;
font-size: 1em;
}
ul {
margin: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h3>
customized selectable, hoverable resizeable dropdown with multi-line, single-selection and multiple-selection support
</h3>
<div id="crnk" class="custcont">
<div>
<button id="btsm" class="custbtn" value="ssm">switch to multi-select mode</button>
</div>
<div id="hrnk" class="custhead">
current selected option
</div>
<ul id="ranks" class="custlist">
<li class="custopt">option one</li>
<li class="custopt">option two</li>
<li class="custopt">another third long option</li>
<li class="custopt">another fourth long option</li>
</ul>
</div>
Working on a tip calculator with an animation on an h1 tag and a slideDown and slideUp on click on the h2 tags. Problem is, none of the animations are playing and the click event isn't working either.
Here is the HTML file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tip Calculator</title>
<link rel="shortcut icon" href="images/favicon.ico">
<link rel="stylesheet" href="midtermcss.css">
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://code.jquery.com/jquery-1.8.3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<script src="animationJS.js"></script>
</head>
<body>
<section id="faqs">
<h1>Tip facts</h1>
<h2>Things to know before you tip</h2>
<div>
<p>Tips Account for 44 Billion dollars of the Food Industry</p>
<p>7 States require servers to be paid minimum wage like everyone else</p>
<ul>
<li>Minnessota</li>
<li>Montana</li>
<li>Washington</li>
<li>Oregon</li>
<li>California</li>
<li>Nevada</li>
<li>Alaska</li>
</ul>
<p>Current Federal minimum tipped wage is $2.13 per hour can you live on that?</p>
<p>Charging with Credit/Debit cards tends to reduce the average tip</p>
</div>
</section>
<section id="js">
<h1 id="heading">Tip Calculator</h1>
<label for="billAmount">Total Amount Of Bill:</label>
<input type="text" id="billAmount"><br>
<label for="percentTip">Percent To Tip:</label>
<input type="text" id="percentTip"><br>
<label for="amountPeople">How Many People?:</label>
<input type="text" id="amountPeople"><br>
<label for="totalTip">Tip Total:</label>
<input type="text" id="totalTip"><br>
<label> </label>
<input type="button" id="calculate" value="Calculate"><br>
</section>
</body>
</html>
Here is the JS file.
$(document).ready(function() {
// runs when an h2 heading is clicked
$("#faqs h2").toggle(
function() {
$(this).toggleClass("minus");
$(this).next().slideDown(1000, "easeOutBounce");
},
function() {
$(this).toggleClass("minus");
$(this).next().slideUp(1000, "easeInBounce");
}
);
$("#faqs h1").animate({
fontSize: "400%",
opacity: 1,
left: "+=375"
}, 1000, "easeInExpo")
.animate({
fontSize: "175%",
left: "-=200"
}, 1000, "easeOutExpo");
$("#faqs h1").click(function() {
$(this).animate({
fontSize: "400%",
opacity: 1,
left: "+=375"
}, 2000, "easeInExpo")
.animate({
fontSize: "175%",
left: 0
}, 1000, "easeOutExpo");
});
});
var $ = function(id) {
return document.getElementById(id);
}
var calculateClick = function() {
var billAmount = parseFloat($("billAmount").value);
var percentTip = parseFloat($("percentTip").value);
var amountPeople = parseInt($("amountPeople").value);
if (isNaN(billAmount) || billAmount <= 0) {
alert("Your bill can't be 0 or less.");
} else if (isNaN(percentTip) || percentTip <= 0) {
alert("The percentage should be a whole number.");
} else if (isNaN(amountPeople) || amountPeople <= 0) {
alert("You are 1 person never count yourself as less.");
} else {
var total = billAmount * (percentTip / 100) / amountPeople;
$("totalTip").value = total.toFixed(2);
}
}
window.onload = function() {
$("calculate").onclick = calculateClick;
$("billAmount").focus();
}
Last but not least the CSS file since the open and minus classes are listed in there
* {
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
background-color: white;
margin: 0 auto;
width: 500px;
border: 3px solid blue;
}
section {
padding: 0 1em .5em;
}
section.js {
padding: 0 1em .5em;
}
h1 {
text-align: center;
margin: .5em 0;
}
label {
float: left;
width: 10em;
text-align: right;
}
input {
margin-left: 1em;
margin-bottom: .5em;
}
#faqs h1 {
position: relative;
left: -168px;
font-size: 125%;
color: blue;
}
h2 {
font-size: 120%;
padding: .25em 0 .25em 25px;
cursor: pointer;
background: url(images/plus.png) no-repeat left center;
}
h2.minus {
background: url(images/minus.png) no-repeat left center;
}
div.open {
display: block;
}
ul {
padding-left: 45px;
}
li {
padding-bottom: .25em;
}
p {
padding-bottom: .25em;
padding-left: 25px;
}
I can't figure out for the life of me why the animations work in a separate test file but when I use them now in my tip calculator they don't. I'm using Murach's Javascript and Jquery book but this section has been terribly hard to understand.
Your issue is that you include jQuery but later on in the global scope you redefine the $:
var $ = function(id) {
return document.getElementById(id);
}
Fiddle: http://jsfiddle.net/AtheistP3ace/u0von3g7/
All I did was change the variable name holding that function and replace it in the areas you were using it. Specifically:
var getById = function(id) {
return document.getElementById(id);
}
var calculateClick = function() {
var billAmount = parseFloat(getById("billAmount").value);
var percentTip = parseFloat(getById("percentTip").value);
var amountPeople = parseInt(getById("amountPeople").value);
if (isNaN(billAmount) || billAmount <= 0) {
alert("Your bill can't be 0 or less.");
} else if (isNaN(percentTip) || percentTip <= 0) {
alert("The percentage should be a whole number.");
} else if (isNaN(amountPeople) || amountPeople <= 0) {
alert("You are 1 person never count yourself as less.");
} else {
var total = billAmount * (percentTip / 100) / amountPeople;
getById("totalTip").value = total.toFixed(2);
}
}
window.onload = function() {
getById("calculate").onclick = calculateClick;
getById("billAmount").focus();
}
$ is just shorthand for jQuery. When you include jQuery it creates two functions for you that both do the same thing. jQuery and $. If you set $ equal to something else you have effectively overwritten jQuery library included in your page and it will no longer operate as you would expect. All jQuery functionality begins with using $ or jQuery function. Once that returns a jQuery object to you, you can begin chaining and calling functions off those objects but to get a jQuery object you need to use the jQuery or $ function.
You mentioned in a comment above your teacher had you do that to fix something. I imagine it was because jQuery was not initially included so he just created the $ selector function to get you moving but I would hope he explained why he did that and how it can affect things later.