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>
Related
I would like to have a form that:
Display validation messages in a custom format instead of the default style.
Display all invalid field bubbles at once instead of one at a time.
Right now, I am stuck with the boring browser-specific message appearance and I don't see the next error until I correct the last one. This is a really bad user experience, so looking for a few pointers on how to address this.
This is my current JavaScript code:
const contactUsForm = document.querySelector('#Form');
if (contactUsForm) {
function Validate() {
validatedFields = contactUsForm.querySelectorAll('[data-validation-required],[data-validation-format]');
validatedFields.forEach(field => {
/* RegEx patterns */
const emailPattern = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
if (field.getAttribute('type') === 'email')
{
field.setAttribute('pattern', emailPattern);
}
if (field.validity.valueMissing) {
field.setCustomValidity(field.dataset.validationRequired);
}
else if (field.validity.patternMismatch) {
field.setCustomValidity(field.dataset.validationFormat);
}
else {
field.setCustomValidity('');
}
field.reportValidity();
contactUsForm.checkValidity();
/* Recheck on field value change */
field.addEventListener('change', function() {
field.setCustomValidity('');
Validate();
});
});
}
Validate();
contactUsForm.addEventListener('submit', function(e) {
e.preventDefault;
if (e.checkValidity() == false) {
return false;
}
else {
// form.submit()
}
});
}
Styling validation bubbles/tooltips used to be feature but only exclusive to Chrome, however it got removed.
More information about it here:
How do you style the HTML5 form validation messages?
However, you can create your own tooltips or bubbles to display validation messages. With use of a div container and a span and a little bit of CSS, you can create a bubble with almost any kind of look you can image.
.ttCont {
position: relative;
display: inline-block;
}
.ttCont .ttText {
display: inline-block;
visibility: hidden;
min-width: 200px;
background-color: darkblue;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
opacity: 0;
transition: opacity 0.5s;
/* Place bubble to the right of container */
position: absolute;
z-index: 1;
top: 5px;
left: 105%;
}
.ttCont .ttText::after {
content: " ";
position: absolute;
top: 50%;
right: 100%; /* To the left of the bubble */
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent darkblue transparent transparent;
}
.ttCont .ttText.active{
visibility: visible;
opacity: 1;
}
Use of custom bubbles now means that you might need to use less of the ValidityState API, however you can still validate your fields using the same approach. Instead of using field.reportValidity(), you can create a custom function that shows a bubble whenever each field validates
function customReportValidatity(elem, type) {
let msg = "";
///check if validity is based on required or mismatch///
switch (type) {
case 'required':
msg = $(elem).attr('data-validation-required');
///without jQuery
// msg = elem.dataset.validationRequired;
break;
case 'format':
msg = $(elem).attr('data-validation-format');
///without jQuery
// msg = elem.dataset.validationFormat;
break;
default:
break;
}
///make popup appear///
let ttText = $(elem).parent().children('.ttText');
$(ttText).text(msg);
$(ttText).addClass('active');
///without jQuery
// let ttText = elem.parentElement.querySelector('.ttText');
// ttText.innerText = msg;
// ttText.classList.add('active');
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
With that styling and custom function, you can apply it to your existing code for any form you are willing to apply to. In this case, I made an example form that takes in a name and email that are required, while the description is not required.
const contactUsForm = document.querySelector('#Form');
const submitBtn = document.querySelector('#submitBtn');
if (contactUsForm) {
function customReportValidatity(elem, type) {
let msg = "";
///check if validity is based on required or mismatch///
switch (type) {
case 'required':
msg = $(elem).attr('data-validation-required');
///without jQuery
// msg = elem.dataset.validationRequired;
break;
case 'format':
msg = $(elem).attr('data-validation-format');
///without jQuery
// msg = elem.dataset.validationFormat;
break;
default:
break;
}
///make popup appear///
let ttText = $(elem).parent().children('.ttText');
$(ttText).text(msg);
$(ttText).addClass('active');
///without jQuery
// let ttText = elem.parentElement.querySelector('.ttText');
// ttText.innerText = msg;
// ttText.classList.add('active');
}
function Validate() {
let isValid = true;
/* RegEx patterns */
const emailPattern = /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))#((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i;
let validatedFields = contactUsForm.querySelectorAll('[data-validation-required],[data-validation-format]');
validatedFields.forEach(field => {
if (field.getAttribute('type') === 'email') {
field.setAttribute('pattern', emailPattern);
}
if (field.validity.valueMissing) {
field.setCustomValidity(field.dataset.validationRequired);
customReportValidatity(field, 'required');
isValid = false;
} else if (field.validity.typeMismatch) {
//using typeMismatch instead of patternMismatch because the regex is not working for emails
field.setCustomValidity(field.dataset.validationFormat);
customReportValidatity(field, 'format');
isValid = false;
}
contactUsForm.checkValidity();
/// Recheck on field value change ///
field.addEventListener('change', function() {
$('.ttText').removeClass('active');
///without jquery
/*document.querySelectorAll('.ttText').forEach((tt)=>{
tt.classList.remove('active');
});*/
Validate();
});
});
return isValid;
}
submitBtn.addEventListener('click', function(e) {
e.preventDefault();
e.stopPropagation();
if (Validate()) {
// contactUsForm.submit();
//you can use this output to check if the form will submit
console.log("Form Submitted!");
} else {
return false;
}
});
}
.ttCont {
position: relative;
display: inline-block;
}
.ttCont .ttText {
display: inline-block;
visibility: hidden;
min-width: 200px;
background-color: darkblue;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px;
opacity: 0;
transition: opacity 0.5s;
position: absolute;
z-index: 1;
top: 5px;
left: 105%;
}
.ttCont .ttText::after {
content: " ";
position: absolute;
top: 50%;
right: 100%;
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent darkblue transparent transparent;
}
.ttCont .ttText.active {
visibility: visible;
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.0/jquery.min.js"></script>
<form id="Form">
<div class="ttCont">
<label for="name">Name</label><br/>
<input type="text" name="name" required data-validation-required="Name is required!" /><br/>
<span class="ttText"></span>
</div>
<br/><br/>
<div class="ttCont">
<label for="email">Email</label><br/>
<input type="email" name="email" required data-validation-required="Email is required" data-validation-format="Email must have the format similar to example#email.com!" /><br/>
<span class="ttText"></span>
</div>
<br/><br/>
<div class="ttCont">
<label for="desc">Description</label><br/>
<input type="text" name="desc" data-validation-required="Description is required!" /><br/>
<span class="ttText"></span>
</div>
<br/><br/>
<button type="button" id="submitBtn">Submit</button>
</form>
You can read more about how to create and handle custom tooltips here:
W3Schools Tooltips
LogRocket Tutorial
I have this code and the problem is the "main" and "left/right" image are not being displayed. I have gone through the entire logic and can't figure out what is wrong.
The code works if I replace else with else if(flag===1 && child[I].id=="left" || child[I].id=="right")
Expected output:
movement of mouse should change the image, this change is determined by the height of the <body> element.
Output getting:
only one image is displayed and does not changes no mater where I move my mouse.
[ also changing event from "mouseover" to "mousemove" does not help ]
so why isn't it working when I remove it, shouldn't at least "main" work??
PS: I'm unable to provide an image, please find another one.
'use strict';
(function() {
var wlen = screen.availWidth;
var whet = screen.availHeight;
var last = {};
var chk = 3;
var eye = document.getElementsByClassName("eyes");
var build = function(ele) {
if (ele.previousElementSibling !== null) ele.previousElementSibling.style = "none";
last = ele.style;
last.display = "block";
}
var anite = function() {
var x = event.screenX;
var y = event.screenY;
last.display = "none";
var child;
var flag;
y < whet / 3 ? flag = 2 : (y > 2 * whet / 3) ? flag = 0 : flag = 1;
x < wlen / 2 ? child = eye[1].childNodes : child = eye[0].childNodes;
for (let i = 0; i < child.length; i++) {
if (child[i].id) {
if (child[i].id == "main" && flag === 0) {
build(child[i]);
} else if (child[i].id.search(/a/i) === 0 && flag === 2) {
build(child[i]);
} else {
build(child[i]);
}
}
}
}
document.addEventListener("mouseover", anite, false);
})()
html,body {
height: 100%;
}
body {
display: flex;
align-items: center;
}
main {
width: 100%;
overflow: hidden;
}
.eyes {
width:50%;
float: left;
height: 300px;
}
#main {
background: url('eyes.png') no-repeat 0 0;
width: 1000px;
height: 254px;
display:none;
}
#left {
background: url('eyes.png') no-repeat 0 -254px;
width: 1000px;
height: 254px;
display:none;
}
#aLeft {
background: url('eyes.png') no-repeat 0 -508px;
width: 1000px;
height: 254px;
display:none;
}
#right {
background: url('eyes.png') no-repeat 0 -762px;
width: 1000px;
height: 254px;
display:none;
}
#aRight {
background: url('eyes.png') no-repeat 0 -1016px;
width: 1000px;
height: 280px;
display:none;
}
<html>
<head>
<title>Student Details</title>
</head>
<body>
<main>
<div class="eyes">
<!-- <img src="eyes.png" alt="eyes"> -->
<p id="main"></p>
<p id="right"></p>
<p id="aRight"></p>
</div>
<div class="eyes">
<p id="main"></p>
<p id="left"></p>
<p id="aLeft"></p>
</div>
</main>
</body>
</html>
I've tried to setup a very "vanilla" approach to this but cannot get the result.
I'm trying to reach into the DOM and the associated div styles using JS and effectively change the "display" property of the CSS.
The JS is error free but the CSS doesn't change.
(function() {
var singleCard = document.getElementById('card-container');
var manyCard = document.getElementById('card-container-many');
var allCard = document.getElementById('card-container') && document.getElementById('card-container-many');
var singleCardCss = document.querySelector('#card-container');
var manyCardCss = document.querySelector('#card-container-many');
var allCardCss = document.querySelector('#card-container') && document.querySelector('#card-container-many');
if (singleCardCss.display && manyCardCss.display === 'none') {
allCardCss.display = 'block';
} else {
allCardCss.display = 'none';
}
}());
#card-container {
position: relative;
display: none;
width: 280px;
height: 310px;
background-size: 640px 360px;
background-repeat: no-repeat;
border: 1px solid #222;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
#card-container-many {
position: relative;
display: none;
width: 280px;
height: 310px;
background-size: 640px 360px;
background-repeat: no-repeat;
border: 1px solid #222;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
<div class="container-fluid text-center">
<div id="card-container"></div>
</div>
<div class="container-fluid text-center">
<div id="card-container-many"></div>
</div>
The .style property is missing. For example:
allCardCss.style.display = 'block';
Also, the use of the AND operator is wrong I believe. It should be used in the if condition like so:
if (singleCardCss.style.display === "none" && manyCardCss.style.display === 'none') {...
Each side of the operand must be complete in a conditional even when it's a comparison between 2 objects (singleCardCSS and manyCardCSS) vs. the same condition ("none").
I took a third look and saw that allCardCSS is wrong as well, it should be:
var allCardCSS = document.querySelectorAll('div > div');
The result will be a NodeList of all divs that are a child of another div (singleCardCSS and manyCardCSS). This NodeList is an array-like object which you can do simple iterations upon in order to access the objects within. Notice how the for loop goes through the NodeList allCardCss.
Finally on the last statement has been eliminated because the else isn't needed since they are already .style.display="none". The first statements have been eliminated as well because .getElementById('ID') is identical to querySelector('#ID');
One last thing, I almost forgot about the parenthesis business:
Either of the following two patterns can be used to immediately invoke
a function expression, utilizing the function's execution context to
create "privacy."
(function(){ /* code */ }()); // Crockford recommends this one
(function(){ /* code */ })(); // But this one works just as well
-Ben Alman
So you are ok. The point is that if you have a set of extra parenthesis at the end then that'll be interpreted by the browser as an Expression Function which causes an Immediate Invocation*. The mention of privacy is referring to the IIFE with a closure which doesn't apply in your circumstance unless you make the latter part of the code into a function in which case you have a closure. In your case it's not needed since you aren't passing any variables from the outside of your function.
To those more knowledgeable. If there's anything I've said that to the contrary or omitted, please leave a comment with your downvote.
*it's IIFE a little mixed up in order in sentence but you get the picture 😉
Demo
(function() {
var singleCardCss = document.querySelector('#card-container');
var manyCardCss = document.querySelector('#card-container-many');
var allCardCss = document.querySelectorAll('div > div');
if (singleCardCss.style.display === "none" && manyCardCss.style.display === 'none') {
for (let i = 0; i < allCardCSS.length; i++) {
allCardCss[i].style.display = 'block';
}
}
}());
#card-container {
position: relative;
display: none;
width: 280px;
height: 310px;
background-size: 640px 360px;
background-repeat: no-repeat;
border: 1px solid #222;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
#card-container-many {
position: relative;
display: none;
width: 280px;
height: 310px;
background-size: 640px 360px;
background-repeat: no-repeat;
border: 1px solid #222;
border-radius: 10px;
margin: 10px;
cursor: pointer;
}
<div class="container-fluid text-center">
<div id="card-container"></div>
</div>
<div class="container-fluid text-center">
<div id="card-container-many"></div>
</div>
Your error is very common. You have to remove the last ) after your function. You close your IIFE after calling it. You can try but your function will be never call! You also can try to delete your variable allCardCss and allCard. I do not understand why do you initialize them with &&.
Replace:
(function() {
var singleCard = document.getElementById('card-container');
var manyCard = document.getElementById('card-container-many');
var allCard = document.getElementById('card-container') && document.getElementById('card-container-many');
var singleCardCss = document.querySelector('#card-container');
var manyCardCss = document.querySelector('#card-container-many');
var allCardCss = document.querySelector('#card-container') && document.querySelector('#card-container-many');
if (singleCardCss.display && manyCardCss.display === 'none') {
singleCardCss.display = 'block';
} else {
allCardCss.display = 'none';
}
}());
By:
(function() {
var singleCard = document.getElementById('card-container');
var manyCard = document.getElementById('card-container-many');
var singleCardCss = document.querySelector('#card-container');
var manyCardCss = document.querySelector('#card-container-many');
if (singleCardCss.display && manyCardCss.display === 'none') {
singleCardCss.display = 'block';
manyCardCss.display = 'block';
} else {
singleCardCss.display = 'none';
manyCardCss.display = 'none';
}
})();
I found this article that's supposed to be related to what I was looking for, which is sorting a list by class. However, in my code, it didn't work. So I'm trying to figure out how to solve the problem. I have two classes, "offline" and "none". I want the class "offline" to come at top and the class "none" to appear at bottom under "offline" area. I have one more class in each div's which is "indbox", therefore, I tried to use "getElementsByClassName" but it's not working.
Here's my code from codepen.
$(document).ready(function() {
$(".con-button").click(function(){
var cssObj = {};
cssObj.left = $(this).position().left;
cssObj.width = $(this).outerWidth();
$(".controls .effect").css( cssObj );
if(this.id == "c-all") {
$('.offline').hide();
$('.offline').fadeIn("slow").show();
$('.online').hide();
$('.online').fadeIn("slow").show();
$('.none').fadeIn("slow").show();
} else if (this.id == "c-online") {
$('.offline').hide();
$('.online').hide();
$('.online').fadeIn("slow").show();
$('.none').hide();
} else if (this.id == "c-offline") {
$('.offline').hide();
$('.offline').fadeIn("slow").show();
$('.online').hide();
$('.none').hide();
}
});
$(".con-button").eq(0).trigger("click");
getSteams();
var elem = $('#offline').find('div').sort(sortMe);
function sortMe(a, b) {
return a.getElementsByClassName("offline") < b.getElementsByClassName("none");
}
$('#offline').append(elem);
});
var channels = ["BasicallyIDoWrk", "FreeCodeCamp", "Golgothus", "OgamingSC2", "maatukka", "Vinesauce", "brunofin", "comster404", "esl_csgo"];
var cb = "?client_id=egn4k1eja0yterrcuu411n5e329rd3&callback=?";
function getSteams() {
channels.forEach(function(indchannel) {
//for (var channel in channels) {
//var indchannel = channel;
var streamURL = "https://api.twitch.tv/kraken/streams/" + indchannel + cb;
var channelURL = "https://api.twitch.tv/kraken/channels/" + indchannel + cb;
$.ajax({
url: streamURL,
type: 'GET',
dataType: "jsonp",
data: {
//action: 'query',
format: 'json',
},
headers: {
"Accept": "application/vnd.twitchtv.v5+json",
},
success: function(data) {
var game;
var status;
if(data.stream === null) {
$.getJSON(data._links.channel + "/?client_id=egn4k1eja0yterrcuu411n5e329rd3&callback=?", function(data2) {
if(data2.status == 404) {
game = "The Account doesn't exist";
status = "none";
} else {
game = "Offline";
status = "offline";
}
$("#offline").append('<div class="indbox ' + status + '"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>');
} );
} else {
game = data.stream.game;
status = "online";
$("#online").append('<div class="indbox ' + status + '"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>');
};
}
});
});
}
html, body{
height:100%;
margin: 0;
background-color: #ffffff;
}
.wrapper {
text-align: center;
position: relative;
width: 100%;
height: 100%;
display:block;
}
.container {
width: 75%;
margin: 30px auto 0;
position: relative;
}
.logobox img {
width: 20%;
margin: 0 auto;
}
.controls {
position: relative;
width: 100%;
}
.con-button {
position: relative;
background-color: white;
border: none;
margin: 0.5em 0 0 0;
padding: 0.5em 1em 0.5em 1em;
text-align: center;
color: rgb(100,65,164);
font-size: 20px;
transition: .4s;
}
.con-button:hover {
cursor: pointer;
/*border-bottom: 3px solid rgba(224, 217, 236, 1);*/
}
.con-button:focus {outline: 0;}
.divider hr {
border-top: 1px solid #6441A4;
}
.effect {
position: absolute;
display: block;
left: 0;
bottom: 5px;
height: 2px;
width: 60px;
transition: 0.4s ease-in-out;
/*border-bottom: 3px solid rgba(100, 65, 164, 1);*/
background: #6441A4;
}
.indbox {
width: 100%;
display: block;
margin: 5px 0px;
padding: 8px 0px;
}
.online {
background-color: #98FB98;
}
.offline {
background-color: #ff9999;
}
.none {
background-color: #D3D3D3;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="container">
<div class="twitchtvarea">
<div class="logobox">
<img src="https://s6.postimg.org/bay7stotd/Twitch_Purple_RGB.png" />
</div>
<div class="twitchbox">
<div class="controls">
<button id="c-all" class="con-button active" type="button">All</button>
<button id="c-online" class="con-button" type="button">Online</button>
<button id="c-offline" class="con-button" type="button">Offline</button>
<div class="effect"></div>
</div>
<div class="divider"><hr></div>
<div id="online"></div>
<div id="offline"></div>
</div>
</div>
</div>
</div>
The code I would like for you to focus on is:
var elem = $('#offline').find('div').sort(sortMe);
function sortMe(a, b) {
return a.getElementsByClassName("offline") < b.getElementsByClassName("none");
}
$('#offline').append(elem);
Please help me fix it.
Looking through your code, I find out that you are using thisSort Function; however, your way of doing is incorrect. In the example, they have:
function compare(a, b) {
if (a is less than b by some ordering criterion) {
return -1;
}
if (a is greater than b by the ordering criterion) {
return 1;
}
// a must be equal to b
return 0;
}
So in order to sort "offline" before "none", your function has to return 1
function sortMe(a, b){
if (a.hasClass("offline")) {
return 1; //if an element has offline, move it above.
} else {
return -1; //if an element doesn't have offline, it means it's none. Move it down.
}
}
You might want to add in the condition to check whether they both have offline class.
Your problem can be solved by using the :
appendTo();
method.
instead of the :
append();
method.
I added two additional divs in your html code and made a small change to your javascript code ant it works !!!
the html goes like this :
<div class="divider"><hr></div>
<div id="online"></div>
<div id="offline">
<div class="notconnected"></div>
<div class="nonexisting"></div>
</div>
</div>
and the javascript was changed here :
if(data2.status == 404) {
game = "The Account doesn't exist";
status = "none";
}
else {
game = "Offline";
status = "offline";
}
if(status==="none"){
$('<div class="indbox ' + status + '" id="'+status+'"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>').appendTo(".nonexisting");}
else{
$('<div class="indbox ' + status + '" id="'+status+'"><a target="_blank" href="#">'+ indchannel + '<br/>' + game +'</a></div>').appendTo(".notconnected");
}
} );
The Documentation for the method is here : appendTo()
I've made a simple jQuery script which stores values "voted1", "voted2", "voted3" in localStorage. The problem is that on click it stores all values at the same time, and I need it per click as it should be later called (e.g. if "value3" exists begin jQuery logic...)
I can't figure this out, after weeks of testing..
HTML:
[gallery link="none" size="medium" ids="102,13,27,25,23,15" orderby="rand"]
<div class="exists" style="display: none;">Thank you for voting!</div>
CSS:
.gallery-item a {
background-color: black;
border: 1px solid orange;
border-radius: 6px;
color: orange;
display: inline-table;
font-size: 14px;
font-weight: bold;
max-width: 100%;
width: 32%;
}
.exists {
background-color: black;
border-radius: 18px;
box-shadow: 1px 3px 20px -3px grey inset;
display: block;
height: 32%;
left: 24%;
margin-left: 10%;
margin-right: 10%;
margin-top: 10%;
max-width: 100%;
padding-left: 12%;
padding-top: 6%;
position: fixed;
top: 23%;
width: 36%;
z-index: 999999;
color: olivedrab;
font-weight: bold;
cursor: context-menu;
}
.voted {
background-color: green !important;
}
jQuery:
$(document).ready(function() {
var voteLink = $('.gallery-item a');
var votedYes = $('.exists');
voteLink.one('click', function() {
// localStorage.setItem('voted1', 'yes1');
$(this).text('Thank you!');
$(this).addClass('voted');
})
voteLink.one('click', function() {
// localStorage.setItem('voted2', 'yes2');
$(this).text('Thank you!');
$(this).addClass('voted');
})
voteLink.one('click', function() {
localStorage.setItem('voted3', 'yes3');
$(this).text('Thank you!');
$(this).addClass('voted');
if($('.voted').length === 3){
voteLink.fadeOut('slow');
$('.exists').fadeIn(1800);
}
if (localStorage.getItem("voted3")) {
voteLink.remove();
votedYes.fadeIn(1800);
}
});
As I said, on first click it places all values in localStorage and I need this separated.
Thanks guys.
$(document).ready(function() {
var voteLink = $(".gallery-item a");
var votedYes = $(".exists");
if (localStorage.getItem("count") === null) {
localStorage.setItem("count", 1)
}
if (!(localStorage.getItem("voted3") === "yes3")) {
var i = Number(localStorage.getItem("count")),
fn = function(e) {
if (i < 3) {
localStorage.setItem("voted" + i, "yes" + i);
$(this).text("Thank you! for vote " + i)
.addClass("voted" + i);
localStorage.setItem("count", 1 + i);
i = Number(localStorage.getItem("count"));
} else {
localStorage.setItem("voted" + i, "yes" + i);
$(this).text("Thank you! for vote " + i)
.addClass("voted" + i)
.fadeOut("slow");
if (localStorage.getItem("voted3") === "yes3") {
voteLink.remove();
votedYes.fadeIn(1800);
}
}
};
voteLink.on("click", fn);
} else {
// if `localStorage` has property `"voted3"` and value equals `"yes3"`,
// do stuff
}
})
Caveat: This answer may be completely off, since your question comes without all the details of your use case. However ...
The following code assumes that ...
up to 3 votes shall be recorded in localStorage
in order to cast the vote n+1, vote n must have been recorded before.
Either register the handlers contingent on the content in localStorage:
if (
localStorage.getItem("voted1")
&& !localStorage.getItem("voted2")
) {
voteLink.one('click', function() {
localStorage.setItem('voted2', 'yes2');
//...
});
}
... or test the localStorage contents inside your event handler:
fn_vote2 = function() {
if (
localStorage.getItem("voted1")
&& !localStorage.getItem("voted2")
) {
localStorage.setItem('voted2', 'yes2');
//...
voteLink.off('click', fn_vote2);
}
};
voteLink.on('click', fn_vote2);
The generalization for vote1, vote3 should come easy. Note that the latter solution implies that you register the handler not just for a single event. Instead you deregister it upon success.
The advantage of the method is the option for cascaded voting without reloading the page.
Btw, since localStorage persists over sessions, it is advisable not to use generic keys like vote<n>.