Difference between ::after and ::before in CSS - javascript

I'm following this tutorial on building a javascript calculator to learn a little more about web development. The CSS code below adds styling when pressing an operator (+, -, *, /) key. When an operator key is pressed, it should darken until another key is pressed to show that the operator is "active". However, when pressing the same operator key over and over again, there should still be some visual indication of the repeated clicking.
const calculator = document.querySelector(".calculator");
const keys = calculator.querySelector(".calculator__keys");
const display = document.querySelector(".calculator__display");
keys.addEventListener('click', e => {
if (e.target.matches('button')) {
const key = e.target;
const action = key.dataset.action;
const keyValue = key.textContent;
const displayNum = display.textContent;
Array.from(key.parentNode.children).forEach(k=>k.classList.remove('is-depressed'));
if (!action) {
if (displayNum === "0" || calculator.dataset.previousKeyType === "operator") {
display.textContent = keyValue;
calculator.dataset.previousKeyType = null;
} else {
display.textContent = displayNum + keyValue;
}
calculator.dataset.previousKeyType = "number";
console.log("Number key")
}
if (action === "decimal") {
if (calculator.dataset.previousKeyType === "operator") {
display.textContent = "0.";
calculator.dataset.previousKeyType = "decimal";
} else if (!displayNum.includes(".")) {
display.textContent += ".";
}
}
if (
action === 'add' ||
action === 'subtract' ||
action === 'multiply' ||
action === 'divide'
) {
calculator.dataset.previousKeyType = "operator";
calculator.dataset.previousNum = displayNum;
calculator.dataset.operator = action;
key.classList.add("is-depressed");
}
if (action === "clear") {
calculator.dataset.previousKeyType = "clear";
console.log("AC key");
}
if (action === "calculate") {
calculator.dataset.previousKeyType = "calculate";
const secondVal = displayNum;
const firstVal = calculator.dataset.previousNum;
const operator = calculator.dataset.operator;
display.textContent = calculate(firstVal, secondVal, operator);
}
}
});
function calculate(firstVal, secondVal, operator) {
let num1 = +firstVal;
let num2 = +secondVal;
if (operator === "add") {
return num1 + num2;
} else if (operator === "subtract") {
return num1 - num2;
} else if (operator === "multiply") {
return num1 * num2;
} else {
return num1 / num2;
}
}
// NOTE: You don't need to mess around with
// CSS to follow the tutorial. Focus on the
// JavaScript instead!
// =========================
// Some personal resets
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
}
/* Responsive Images */
embed,
iframe,
img,
object,
video {
max-width: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6,
ul,
ol,
li,
p,
pre,
blockquote,
figure,
hr {
margin: 0;
padding-right: 0;
padding-left: 0;
}
a {
text-decoration: none;
}
a:focus {
outline: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
display: block;
}
/* Removes all decimals and discs from lists */
ol,
ul {
list-style: none;
}
/*
* Completely resets form items
* ----------------------------
* Super hard reset that removes all borders
* and radiuses of all form items (including
* checkboxes and radios)
*/
input,
textarea,
button {
border: 0;
border-radius: 0;
background-color: transparent;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
outline: none;
appearance: none;
text-align: left;
}
input:hover,
input:active,
input:focus,
textarea:hover,
textarea:active,
textarea:focus,
button:hover,
button:active,
button:focus {
outline: none;
}
:root {
font-family: Helvetica, Arial, sans-serif;
}
html {
font-size: 175%;
font-weight: 300;
line-height: 1.3;
}
body {
align-items: center;
background-image: linear-gradient(236deg, #74ebd5, #acb6e5);
display: flex;
height: 100vh;
justify-content: center;
}
.container {
max-width: 20em;
}
.container > p {
text-align: center;
}
.calculator {
border-radius: 12px;
box-shadow: 0 0 40px 0px rgba(0, 0, 0, 0.15);
margin-left: auto;
margin-right: auto;
margin-top: 2em;
max-width: 15em;
overflow: hidden;
}
.calculator__display {
background-color: #222222;
color: #fff;
font-size: 1.714285714em;
padding: 0.5em 0.75em;
text-align: right;
}
.calculator__keys {
background-color: #999;
display: grid;
grid-gap: 1px;
grid-template-columns: repeat(4, 1fr);
}
.calculator__keys > * {
background-color: #fff;
padding: 0.5em 1.25em;
position: relative;
text-align: center;
}
.calculator__keys > *:active::after,
.calculator__keys > .is-depressed::before {
background-color: rgba(0, 0, 0, 0.2);
bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
content: "";
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.key--operator {
background-color: #eee;
}
.key--equal {
background-image: linear-gradient(to bottom, #fe886a, #ff7033);
grid-column: -2;
grid-row: 2 / span 4;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Calculator</title>
<link rel="stylesheet" href="calculator.css" type="text/css">
<script type="text/javascript" src="calculator.js" defer></script>
</head>
<body>
<div class="container">
<p>
This component works exactly like the calculator you know. Click any number to start calculating!
</p>
<div class="calculator">
<div class="calculator__display">0</div>
<div class="calculator__keys">
<button class="key--operator" data-action="add">+</button>
<button class="key--operator" data-action="subtract">-</button>
<button class="key--operator" data-action="multiply">×</button>
<button class="key--operator" data-action="divide">÷</button>
<button>7</button>
<button>8</button>
<button>9</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button>0</button>
<button data-action="decimal">.</button>
<button data-action="clear">AC</button>
<button class="key--equal" data-action="calculate">=</button>
</div>
</div>
</div>
</body>
</html>
The is-depressed class is a class I add to the operator key element when it is clicked. The code above doesn't give the visual indicator of multiple clicks, but when I change *:active::before to *:active::after, the code does what I want. Why is that?
.calculator__keys > *:active::after,
.calculator__keys > .is-depressed::before {
background-color: rgba(0, 0, 0, 0.2);
bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
content: "";
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}

The answer is simple. Having :active::after and .is-depressed::before means that when your element is pressed you will change the before element AND when you click again you will change the after element so two different elements.
When having both of them as before then it's one element and it is already updated via .is-depressed so the active state will do nothing.
To illustrate this with a more simple example:
.box {
border:1px solid;
width:150px;
height:150px;
display:inline-block;
position:relative;
}
.box:hover::before,
.box.active::before{
content:"";
display:block;
height:50%;
background:red;
}
<div class="box"></div>
<div class="box active"></div>
In the above, nothing will happen when you hover on the second element because active already did the job:
.box {
border:1px solid;
width:150px;
height:150px;
display:inline-block;
position:relative;
}
.box:hover::after,
.box.active::before{
content:"";
display:block;
height:50%;
background:red;
}
<div class="box"></div>
<div class="box active"></div>
In the last exmple the active will add the before element and the hover will add the after element. As simple as that.

The problem with your code is that ::before has position: absolute but the buttons don't, so it's position is relative to the body. Add position: relative or absolute to the buttons.
Regarding your question, they are the same, they're virtual elements (pseudo-elements) (they don't belong to the DOM but they are rendered as if they were regular elements if they have a content property), the difference between them is that ::before is placed at the beginning of the element and ::after at the end.

Related

How to change active tab back to not active after validation failure in JavaScript

I have a Details tab with a dropdown that swaps forms in and out and a Result tab that displays the result in a graph. When the user selects "Result" I want to validate the data entry before going to the result tab.
What happens is that when the Result tab is clicked it becomes active and changes color. I then goes into script.js displayResultContent() which validates the data entry. If the data is not valid I don't want to proceed to the result tab so I only show the result if the data is okay.
The issue I have is that if the data is invalid, error messages are displayed on the form, but the Result tab remains displayed with the active color instead of changing to the non active color. Once the user clicks the mouse on the screen the tab changes color.
Is there any way to make this tab change to the non active color without having to click on the screen? I haven't yet put the media queries in so this looks best on a wide screen, otherwise the tabs are displayed below each other instead of beside. It still works though.
const DATE_TYPE_PAST = 0;
const DATE_TYPE_FUTURE = 1;
const SUCCESS = 0;
const ERROR = 1;
$(function() {
$('#details-tab').click(displayDetails);
$('#result-tab').click(displayResultContent);
$("#your-details-tab").click(displayYourDetailsTab);
$("#your-superannuation-tab").click(displayYourSuperannuationTab);
});
function displayYourDetailsTab() {
removeAllForms();
var form = $("#your-details-form");
form.show();
$('#details-tab').html("Your Details");
}
function displayYourSuperannuationTab() {
removeAllForms();
var form = document.getElementById("your-superannuation-form");
form.style.display = 'block';
$('#details-tab').html("Your Superannuation");
}
function removeAllForms() {
var forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
forms[i].style.display = "none";
}
}
function displayDetails() {
$('#details').show();
$('#result').hide();
$('#result-tab').removeClass('active');
$('#details-tab').addClass('active');
}
function displayResultContent() {
// FIRST CHECK DATA ENTRY
const dateResult = checkDate(document.getElementById("date-of-birth"), DATE_TYPE_PAST);
const rentResult = checkMandatoryWholeNumber(document.getElementById("fortnightly-rent"), "Fortnightly rent ", 0, 999);
if (dateResult === SUCCESS && rentResult === SUCCESS) {
$('#result').show();
$('#details').hide();
$('#result-tab').addClass('active');
$('#details-tab').removeClass('active');
}else {
$('#result-tab').removeClass('active');
}
}
const showError = (input, message) => {
// get the form-field element
let formField = input.closest(".form-field");
// add the error class
formField.classList.remove('success');
formField.classList.add('error');
// show the error message
const error = formField.querySelector('small');
error.textContent = message;
};
const showSuccess = (input) => {
// get the form-field element
let formField = input.closest(".form-field");
// remove the error class
formField.classList.remove('error');
formField.classList.add('success');
// hide the error message
const error = formField.querySelector('small');
error.textContent = '';
};
const yourDetailsForm = document.getElementById("your-details-form");
if (yourDetailsForm != null) {
yourDetailsForm.addEventListener('input', function(e) {
switch (e.target.id) {
case 'date-of-birth':
checkDate(document.getElementById(e.target.id), DATE_TYPE_PAST);
break;
case 'fortnightly-rent':
checkMandatoryWholeNumber(document.getElementById(e.target.id), "Fortnightly rent ", 0, 999);
break;
}
});
}
const isRequired = value => value !== '';
const isValidDate = function(date) {
return (date !== "Invalid Date") && date !== ("Nan");
}
function checkDate(dateElement, dateType) {
const val = dateElement.value;
const newDate = new Date(val);
if (isValidDate(newDate)) {
const today = new Date();
today.setHours(0,0,0,0);
if (dateType === DATE_TYPE_PAST) {
if (newDate < today) {
// okay
showSuccess(dateElement);
return SUCCESS;
}else {
// error
showError(dateElement, "date must be in the past");
return ERROR;
}
} if (dateType === DATE_TYPE_FUTURE) {
if (newDate >= today) {
// okay
showSuccess(dateElement);
return SUCCESS;
}else {
// error
showError(dateElement, "date must be in the future");
return ERROR;
}
}
}else {
showError(dateElement, "date is mandatory");
return ERROR;
}
}
$(document).on("keydown", ".whole-number", function (e) {
const invalidChars = [
"-",
"+",
"e",
".",
];
if (invalidChars.includes(e.key)) {
e.preventDefault();
}
});
function checkMandatoryWholeNumber(element, prefix, min, max) {
if (!isRequired(element.value)) {
showError(element, prefix + " is mandatory");
return ERROR;
}
const val = parseInt(element.value);
if (val < min || val > max) {
showError(element, prefix + " must be between " + min + " and " + max);
return ERROR;
}
showSuccess(element);
return SUCCESS;
}
html, body {
height: 100%;
margin: 0;
overflow:hidden;
}
.content {
height:100%;
margin-left: 15%;
margin-right: 15%;
display:flex;
flex-flow: column;
}
.content .result-content {
background-color: #FFF;
flex: 1 1 auto;
margin-left:1%;
margin-right:1%;
display: none;
}
#tabs {
width: 70%;
margin: 0 auto;
padding: 0;
border-bottom: 7px solid #FE6D73;
margin-top: 50px;
}
.form-container {
width: 70%;
height: 98%;
background-color: #FFF;
margin: 0 auto;
overflow-y: auto;
overflow-x: hidden;
padding-bottom: 7px;
box-sizing: border-box;
padding-top: 0px;
}
li {
list-style: none;
}
/*******************************************************************/
/* NAV TABS */
/******************************************************************/
.nav-link {
border-radius: 15px 15px 0px 0px !important;
text-decoration: none;
height: 40px;
font-size: 80%;
background-color: #eeeeee !important;
color: #aaaaaa;
}
.nav-link.active,
.nav-link:active,
.nav-link:hover,
.nav-link:focus {
border-radius: 15px 15px 0px 0px !important;
background-color: #FE6D73 !important;
color: #FEF9EF !important;
}
.nav-link-left {
width: 255px;
}
.nav-link-right {
width: 100px;
}
.nav-tabs>li>ul>li>a {
width: 200px;
}
.dropdown-menu {
z-index: 999999;
}
.nav-tabs>li>ul {
background-color: #FE6D73 !important;
}
/* Remove border from tabs */
.nav-tabs .nav-link {
border: 0px solid transparent;
}
/* This is the dropdown link */
.dropdown-item {
background-color: #FE6D73 !important;
color: #FEF9EF !important;
}
/* Border at bottom of each item in the drop down list */
.dropdown-menu>li {
border-bottom: 1px solid #FEF9EF !important;
}
.nav-tabs>li>ul>li.active>a, .nav-tabs>li>ul>li.active>a:focus, .nav-tabs>li>ul>li>a:hover {
background-color: #FEF9EF !important;
color: #FE6D73 !important;
}
.dropdown-menu {
border-top: 1px solid #FEF9EF;
}
/*******************************************************************/
/* FORMS */
/******************************************************************/
input[type="number"] {
/*80px*/
width: 5em;
font-size: 80%;
}
input[type="date"] {
border: 1px solid #cccccc;
}
input {
border: 1px solid #cccccc;
}
#your-superannuation-form {
display: none;
}
form {
margin-top: 20px;
background-color: beige;
}
.form-group {
margin-top: 20px;
}
.disabled-link {
cursor: not-allowed;
opacity: 0.5;
}
.disabled-text {
opacity: 0.5;
}
.enabled-text {
opacity: 1.0;
}
.enabled-link {
cursor: pointer;
opacity: 1.0;
}
.pointer-not-allowed{
cursor: not-allowed;
}
.pointer-allowed {
cursor: pointer;
}
form label {
font-family: 'opensansreg', 'Verdana', 'Arial';
}
.form-text {
font-family: Georgia;
align-self: start;
}
.button-text {
font-family: Georgia;
font-size:80%;
}
.input-text {
font-family: Consolas, Lucida Console, monospace;
}
:root {
--error-color: #dc3545;
--success-color: #cccccc;
--warning-color: #ffc107;
}
.form-field input:focus {
outline: none;
}
.form-field.error input {
/* border-color: var(--error-color); */
border: 1px solid #dc3545;
}
.form-field.success input {
/* border-color: var(--success-color); */
border: 1px solid #cccccc;
}
.form-field small {
color: var(--error-color);
font-size: 70%;
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<div id="tabs">
<ul class="nav nav-tabs">
<li class="dropdown nav-item">
<a class="nav-link nav-link-left dropdown-toggle active" id="details-tab" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">Details</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" id="your-details-tab" href="#">Your Details</a></li>
<li><a class="dropdown-item" id="your-superannuation-tab" href="#">Your Superannuation</a></li>
</ul>
</li>
<li class="nav-item">
<a class="nav-link nav-link-right" id="result-tab" href="#" tabindex="-1">Result</a>
</li>
</ul>
</div>
<div class="content">
<div class="form-container" id="details">
<form id="your-details-form">
<div class="form-group">
<label for="date-of-birth" class="form-text">Date of Birth</label>
<div class="form-field">
<input type="date" class="form-text" id="date-of-birth" name="date-of-birth">
<small></small>
</div>
</div>
<div class="form-group">
<label for="fortnightly-rent" class="form-text">Fortnightly Rent</label>
<div class="form-field">
<input type="text" class="input-text" size="4" id="fortnightly-rent" name="fortnightly-rent"
onKeyDown="if (this.value.length == 4) this.value = this.value.slice(0, -1);"/>
<small></small>
</div>
</div>
</form>
<form id="your-superannuation-form">
THIS IS YOUR SUPERANNUATION FORM
</form>
</div> <!-- end form-container-->
<div class="result-content" id="result">
THIS IS THE RESULT
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js#3.8.0/dist/chart.min.js"></script>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
Please remove the .nav-link:focus selector from this declaration
.nav-link.active,
.nav-link:active,
.nav-link:hover{
border-radius: 15px 15px 0px 0px !important;
background-color: #FE6D73 !important;
color: #FEF9EF !important;
}
Currently when you click on a tab the focus remains and it goes only after clicking away
.nav-link.active,
.nav-link:active,
.nav-link:hover,
.nav-link:focus <---- It works properly after removing this
{
border-radius: 15px 15px 0px 0px !important;
background-color: #FE6D73 !important;
color: #FEF9EF !important;
}

javascript on a webpage displaying text wrongly

I have JS code on a webpage that loads questions in from mysql db and displays the text . What happens is that it cuts off words at the end of the line and continues the word on the next line at the start. So all text across the screen starts/ends at the same point.
This seems to be the code where it displays the text.
For example the text will look like at the end of a line 'cont' and then on next line at the start 'inue'.
How do i fix this?
var questions = <?=$questions;?>;
// Initialize variables
//------------------------------------------------------------------
var tags;
var tagsClass = '';
var liTagsid = [];
var correctAns = 0;
var isscorrect = 0;
var quizPage = 1;
var currentIndex = 0;
var currentQuestion = questions[currentIndex];
var prevousQuestion;
var previousIndex = 0;
var ulTag = document.getElementsByClassName('ulclass')[0];
var button = document.getElementById('submit');
var questionTitle = document.getElementById('question');
//save class name so it can be reused easily
//if I want to change it, I have to change it one place
var classHighlight = 'selected';
// Display Answers and hightlight selected item
//------------------------------------------------------------------
function showQuestions (){
document.body.scrollTop = 0; // For Safari
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
if (currentIndex != 0) {
// create again submit button only for next pages
ulTag.innerHTML ='';
button.innerHTML = 'Submit';
button.className = 'submit';
button.id = 'submit';
if(quizPage<=questions.length){
//update the number of questions displayed
document.getElementById('quizNumber').innerHTML = quizPage;
}
}
//Display Results in the final page
if (currentIndex == (questions.length)) {
ulTag.innerHTML = '';
document.getElementById('question').innerHTML = '';
if(button.id == 'submit'){
button.className = 'buttonload';
button.innerHTML = '<i class="fa fa-spinner fa-spin"></i>Loading';
}
showResults();
return
}
questionTitle.innerHTML = "Question No:" + quizPage + " "+currentQuestion.question.category_name +"<br/>"+ currentQuestion.question.text;
if(currentQuestion.question.filename !== ''){
var br = document.createElement('br');
questionTitle .appendChild(br);
var img = document.createElement('img');
img.src = currentQuestion.question.filename;
img.className = 'imagecenter';
img.width = 750;
img.height = 350;
questionTitle .appendChild(img);
}
// create a for loop to generate the options and display them in the page
for (var i = 0; i < currentQuestion.options.length; i++) {
// creating options
var newAns = document.createElement('li');
newAns.id = 'ans'+ (i+1);
newAns.className = "notSelected listyle";
var textAns = document.createTextNode(currentQuestion.options[i].optiontext);
newAns.appendChild(textAns);
if(currentQuestion.options[i].file !== ''){
var br = document.createElement('br');
newAns .appendChild(br);
var img1 = document.createElement('img');
img1.src = currentQuestion.options[i].file;
img1.className = 'optionimg';
img1.width = 250;
img1.height = 250;
newAns .appendChild(img1);
newAns .appendChild(br);
}
var addNewAnsHere = document.getElementById('options');
addNewAnsHere.appendChild(newAns);
}
//.click() will return the result of $('.notSelected')
var $liTags = $('.notSelected').click(function(list) {
list.preventDefault();
//run removeClass on every element
//if the elements are not static, you might want to rerun $('.notSelected')
//instead of the saved $litTags
$liTags.removeClass(classHighlight);
//add the class to the currently clicked element (this)
$(this).addClass(classHighlight);
//get id name of clicked answer
for (var i = 0; i < currentQuestion.options.length ; i++) {
// console.log(liTagsid[i]);
if($liTags[i].className == "notSelected listyle selected"){
//store information to check answer
tags = $liTags[i].id;
// tagsClass = $LiTags.className;
tagsClassName = $liTags[i];
}
}
});
//check answer once it has been submitted
button.onclick = function (){
if(button.id == 'submit'){
button.className = 'buttonload';
button.innerHTML = '<i class="fa fa-spinner fa-spin"></i>Loading';
}
setTimeout(function() { checkAnswer(); }, 100);
};
}
//self calling function
showQuestions();
The website is on my local now but i can upload a screenimage if need be and the whole code of the webpage. Or is the issue in html?
edit: here is html/css code
<style>
/*========================================================
Quiz Section
========================================================*/
/*styling quiz area*/
.main {
background-color: white;
margin: 0 auto;
margin-top: 30px;
padding: 30px;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), 0 5px 5px 0 rgba(0, 0, 0, 0.24);
/*white-space: nowrap;*/
}
/*Editing the number of questions*/
.spanclass {
font-size: x-large;
}
#pages{
border: 3px solid;
display: inline-flex;
border-radius: 0.5em;
float: right;
}
#question{
word-break: break-all;
}
/*format text*/
p {
text-align: left;
font-size: x-large;
padding: 10px 10px 0;
}
.optionimg{
border: 2px solid black;
border-radius: 1.5em;
}
/*Form area width*/
/*formatting answers*/
.listyle {
list-style-type: none;
text-align: left;
background-color: transparent;
margin: 10px 5px;
padding: 5px 10px;
border: 1px solid lightgray;
border-radius: 0.5em;
font-weight: normal;
font-size: x-large;
display: inline-grid;
width: 48%;
height: 300px;
overflow: auto;
}
.listyle:hover {
background: #ECEEF0;
cursor: pointer;
}
/*Change effect of question when the questions is selected*/
.selected, .selected:hover {
background: #FFDEAD;
}
/*change correct answer background*/
.correct, .correct:hover {
background: #9ACD32;
color: white;
}
/*change wrong answer background*/
.wrong, .wrong:hover {
background: #db3c3c;
color: white;
}
/*========================================================
Submit Button
========================================================*/
.main button {
text-transform: uppercase;
width: 20%;
border: none;
padding: 15px;
color: #FFFFFF;
}
.submit:hover, .submit:active, .submit:focus {
background: #43A047;
}
.submit {
background: #4CAF50;
min-width: 120px;
}
/*next question button*/
.next {
background: #fa994a;
min-width: 120px;
}
.next:hover, .next:active, .next:focus {
background: #e38a42;
}
.restart {
background-color:
}
/*========================================================
Results
========================================================*/
.circle{
position: relative;
margin: 0 auto;
width: 200px;
height: 200px;
background: #bdc3c7;
-webkit-border-radius: 100px;
-moz-border-radius: 100px;
border-radius: 100px;
overflow: hidden;
}
.fill{
position: absolute;
bottom: 0;
width: 100%;
height: 80%;
background: #31a2ac;
}
.score {
position: absolute;
width: 100%;
top: 1.7em;
text-align: center;
font-family: Arial, sans-serif;
color: #fff;
font-size: 40pt;
line-height: 0;
font-weight: normal;
}
.circle p {
margin: 400px;
}
/*========================================================
Confeeti Effect
========================================================*/
canvas{
position:absolute;
left:0;
top:11em;
z-index:0;
border:0px solid #000;
}
.imagecenter{
display: block;
margin: 0 auto;
}
.buttonload {
background-color: #04AA6D; /* Green background */
border: none; /* Remove borders */
color: white; /* White text */
padding: 12px 24px; /* Some padding */
font-size: 16px; /* Set a font-size */
}
/* Add a right margin to each icon */
.fa {
margin-left: -12px;
margin-right: 8px;
}
#media only screen and (max-width: 900px){
.listyle {
width: 100% !important;
height: auto !important;
}
.imagecenter {
width: 100% !important;
}
.listyle img{
width: inherit !important;
height: unset !important;
}
.ulclass
{
padding:0px !important;
}
}
</style>
<!-- Main page -->
<div class="main">
<!-- Number of Question -->
<div class="wrapper" id="pages">
<span class="spanclass" id="quizNumber">1</span><span class="spanclass">/<?=$count?></span>
</div>
<!-- Quiz Question -->
<div class="quiz-questions" id="display-area">
<p id="question"></p>
<ul class="ulclass" id="options">
</ul>
<div id="quiz-results" class="text-center">
<button type="button" name="button" class="submit" id="submit">Submit</button>
</div>
</div>
</div>
<canvas id="canvas"></canvas>
<script src="https://code.jquery.com/jquery-3.2.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
I'm guessing that #question{ word-break: break-all; } is probably the culprit then? –
CB..yes that fixed it:)

use EventListener to display submit button when a form is filled

I'm a beginner in javascript and we where given a task at uni to create a hangman game using input fields inside an html form . I want to use an event listener to display a submit button when all the input fields are filled and whenever I want to delete a letter the button must obviously go away .
I have written code below that displays input fields in a form container depending on the letter size of the give word (ex. word = "hi" => 2 input fields to fill for "hi" ).My problem is that I have no clue how to create this eventListener function and I would appreciate your help with this .
My code :
function hangman(){
var island = "Rhodes"; //the given word that is supposed to be found
var t = document.createTextNode(shuffleWord(island))
document.getElementById("hidden-word").appendChild(t);
createSpaces(island);
}
function shuffleWord (word){
var shuffledWord = '';
word = word.split('');
while (word.length > 0) {
shuffledWord += word.splice(word.length * Math.random() << 0, 1);
}
return shuffledWord;
}
function createSpaces(text){
for(var i=0;i<text.length;i++){
var space = document.createElement("input");
space.setAttribute("class" , "dash");
document.getElementById("hangman-container").appendChild(space);
}
}
body, html {
background-size: cover;
}
body{
margin: 0;
}
.transparent-box{
border:none;
position:absolute;
top:10%;
left:15%;
background-color:black;
height:500px;
width:70%;
opacity: 0.6;
}
.transparent-box p{
color:white;
text-align:center;
}
.transparent-box h1{
color:white;
position: relative;
text-align:center;
font-size:20px;
top:30px;
}
#hangman-container{
display: block;
position: relative;
width:auto;
top:30%;
left:0%;
background-color: transparent;
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
.dash{
margin:0;
align-items: flex-start;
width:5%;
border:none;
border-radius: 5%;
background-color: turquoise;
color:red;
font-size:30px;
}
.dash:focus{
opacity:0.8;
}
#submitbtn{
display:none;
position: absolute;
top:200%;
left:80%;
float:right;
}
<body onload=hangman()>
<div class = "transparent-box" id = "t-box">
<p>Play here </p>
<h1 id = "hidden-word">The word is : </h1>
<form id = "hangman-container" method="POST">
<button type = "submit" id="submitbtn">Submit</button>
</form>
</div>
</body>
The word is given as a random string and you have to guess the correct word in the above code .
Thank you in advance .
You likely want this
addEventListener on the window.load
addEventListener on the letters
toggle the class
Note I added a hide class to the button to turn it off
function hangman() {
var island = "Rhodes"; //the given word that is supposed to be found
var t = document.createTextNode(shuffleWord(island))
document.getElementById("hidden-word").appendChild(t);
createSpaces(island);
}
function shuffleWord(word) {
var shuffledWord = '';
word = word.split('');
while (word.length > 0) {
shuffledWord += word.splice(word.length * Math.random() << 0, 1);
}
return shuffledWord;
}
function createSpaces(text) {
for (var i = 0; i < text.length; i++) {
var space = document.createElement("input");
space.setAttribute("class", "dash");
document.getElementById("hangman-container").appendChild(space);
}
}
window.addEventListener("load",function() { // on page load
document.getElementById("t-box").addEventListener("input",function(e) { // any input in the t-box
const tgt = e.target; // the actual input
if (tgt.classList.contains("dash")) { // is it a "dash"?
const letters = [...document.querySelectorAll(".dash")]; // get all dash
length = letters.filter(inp => inp.value.trim() !=="").length; // filter on filled in
document.getElementById("submitbtn").classList.toggle("hide",length<letters.length); // toggle hide class if filled
}
})
hangman()
});
body,
html {
background-size: cover;
}
body {
margin: 0;
}
.transparent-box {
border: none;
position: absolute;
top: 10%;
left: 15%;
background-color: black;
height: 500px;
width: 70%;
opacity: 0.6;
}
.transparent-box p {
color: white;
text-align: center;
}
.transparent-box h1 {
color: white;
position: relative;
text-align: center;
font-size: 20px;
top: 30px;
}
#hangman-container {
display: block;
position: relative;
width: auto;
top: 30%;
left: 0%;
background-color: transparent;
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
.dash {
margin: 0;
align-items: flex-start;
width: 5%;
border: none;
border-radius: 5%;
background-color: turquoise;
color: red;
font-size: 30px;
}
.dash:focus {
opacity: 0.8;
}
#submitbtn {
position: absolute;
top: 200%;
left: 80%;
float: right;
}
.hide { display:none}
<div class="transparent-box" id="t-box">
<p>Play here </p>
<h1 id="hidden-word">The word is : </h1>
<form id="hangman-container" method="POST">
<button type="submit" class="hide" id="submitbtn">Submit</button>
</form>
</div>
I have added this fiddle, where I am going through all input fields and adding a listener, then inside that go through each field and based upon it's content I show or hide the submit button.
Fiddle
const inputLists = document.querySelectorAll("input");
let showButton = true;
document.querySelectorAll("input").forEach((el) => {
el.addEventListener('input', (evt => {
inputLists.forEach((ip) => {
console.log(ip.value);
if (ip.value === '') {
showButton = false;
} else {
showButton = true;
}
})
if (showButton) {
document.querySelector('button').style.display = 'block'
} else {
document.querySelector('button').style.display = 'none'
}
}))
})
button {
display: none;
}
<form>
<input type="text">
<input type="text">
<button type="submit">
Submit
</button>
</form>
This one contains another feature. When one field is filled, it gets to the next one automaticly. Good luck.
var island;
function hangman(){
island = "Rhodes"; //the given word that is supposed to be found
var t = document.createTextNode(shuffleWord(island))
document.getElementById("hidden-word").appendChild(t);
createSpaces(island);
}
function shuffleWord (word){
var shuffledWord = '';
word = word.split('');
while (word.length > 0) {
shuffledWord += word.splice(word.length * Math.random() << 0, 1);
}
return shuffledWord;
}
function createSpaces(text){
var spaces = new Array(island.length);
for(var i=0;i<text.length;i++){
let n=i;
spaces[i] = document.createElement("input");
spaces[i].setAttribute("class" , "dash");
spaces[i].maxLength = 1;
spaces[i].oninput = function () {
if (this.length == 0) return;
if (n == island.length-1) document.getElementById("submitbtn").classList.add("show");
if (n < island.length-1) spaces[n+1].focus();
}
document.getElementById("hangman-container").appendChild(spaces[i]);
}
}
body, html {
background-size: cover;
}
body{
margin: 0;
}
.transparent-box{
border:none;
position:absolute;
top:10%;
left:15%;
background-color:black;
height:500px;
width:70%;
opacity: 0.6;
}
.transparent-box p{
color:white;
text-align:center;
}
.transparent-box h1{
color:white;
position: relative;
text-align:center;
font-size:20px;
top:30px;
}
#hangman-container{
display: block;
position: relative;
width:auto;
top:30%;
left:0%;
background-color: transparent;
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
.dash{
margin:0;
align-items: flex-start;
width:5%;
border:none;
border-radius: 5%;
background-color: turquoise;
color:red;
font-size:30px;
}
.dash:focus{
opacity:0.8;
}
#submitbtn{
display:none;
position: absolute;
top:200%;
left:80%;
float:right;
}
#submitbtn.show {
display: inline-block;
}
<body onload=hangman()>
<div class = "transparent-box" id = "t-box">
<p>Play here </p>
<h1 id = "hidden-word">The word is : </h1>
<form id = "hangman-container" method="POST">
<button type = "submit" id="submitbtn">Submit</button>
</form>
</div>
</body>

How can I change color efficiently for right/wrong characters in a typing speed test?

I was doing an exercise involving a typing speed test. I added a color palette that makes the whole text green if the character matches the test text and red if it does not match.
If I wanted individual characters to change though, all the ways I can think of going about this seem quite tedious. I've tried creating individual span tags every time a character is entered and assigning them to the array value of the text string but haven't been able to get it to work. Is there a more efficient way to do this or is my current method the "best" way to go about it?
I would much more prefer a general path to solve this as opposed to someone just writing out a complete solution so I can write it out on my own please.
const testWrapper = document.querySelector(".test-wrapper");
const testArea = document.querySelector("#test-area");
const originText = document.querySelector("#origin-text p").innerHTML;
const resetButton = document.querySelector("#reset");
const theTimer = document.querySelector(".timer");
var interval;
var timer = [0,0,0,0];
var timerRunning = false;
// Add leading zero to numbers 9 or below (purely for aesthetics):
function leadingZero(time) {
if (time <= 9) {
time = "0" + time;
}
return time;
}
// Run a standard minute/second/hundredths timer:
function runTimer() {
let currentTime = leadingZero(timer[0]) + ":" + leadingZero(timer[1]) + ":" + leadingZero(timer[2]);
theTimer.innerHTML = currentTime;
timer[3]++;
timer[0] = Math.floor((timer[3]/100)/60);
timer[1] = Math.floor((timer[3]/100) - (timer[0] * 60));
timer[2] = Math.floor(timer[3] - (timer[1] * 100) - (timer[0] * 6000));
}
// Match the text entered with the provided text on the page:
function spellCheck() {
let textEntered = testArea.value;
let originTextMatch = originText.substring(0,textEntered.length);
if (textEntered == originText) {
testWrapper.style.borderColor = "#429890";
clearInterval(interval);
} else {
if (textEntered == originTextMatch) {
testWrapper.style.borderColor = "#65ccf3";
testArea.style.color = "#00B400";
} else {
testWrapper.style.borderColor = "#e95d0f";
testArea.style.color = "#FF0000"
}
}
}
// Start the timer:
function start() {
let textEnteredLength = testArea.value.length;
if(textEnteredLength === 0 && !timerRunning) {
timerRunning = true;
interval = setInterval(runTimer, 10);
}
}
// Reset everything:
function reset(){
timer = [0,0,0,0];
theTimer.innerHTML = "00:00:00";
clearInterval(interval);
testArea.value = "";
timerRunning = false;
console.log("The reset button has been pressed.");
}
// Event listeners for keyboard input and the reset button:
testArea.addEventListener("keypress", start, false);
testArea.addEventListener("keyup", spellCheck, false);
resetButton.addEventListener("click", reset, false);
/*--------------------------------------------------------------
Typography
--------------------------------------------------------------*/
body,
button,
input,
select,
textarea {
font-family: 'Source Sans Pro', 'Helvetica', Arial, sans-serif;
font-size: 18px;
line-height: 1.5;
}
h1,
h2,
h3,
h4,
h5,
h6 {
clear: both;
}
p {
margin-bottom: 1.5em;
}
b,
strong {
font-weight: bold;
}
dfn,
cite,
em,
i {
font-style: italic;
}
blockquote {
margin: 0 1.5em;
}
address {
margin: 0 0 1.5em;
}
pre {
background: #eee;
font-family: "Courier 10 Pitch", Courier, monospace;
font-size: 15px;
font-size: 1.5rem;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
padding: 1.6em;
}
code,
kbd,
tt,
var {
font: 15px Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace;
}
abbr,
acronym {
border-bottom: 1px dotted #666;
cursor: help;
}
mark,
ins {
background: #fff9c0;
text-decoration: none;
}
sup,
sub {
font-size: 75%;
height: 0;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
bottom: 1ex;
}
sub {
top: .5ex;
}
small {
font-size: 75%;
}
big {
font-size: 125%;
}
/*--------------------------------------------------------------
Layout
--------------------------------------------------------------*/
body {
margin: 0;
padding: 0;
}
.masthead {
padding: 1em 2em;
background-color: #0D1B2E;
color: white;
}
.masthead h1 {
text-align: center;
}
.intro {
padding: 2em 2em;
color: #ffffff;
background: #429890;
}
.intro p,
.test-area {
margin: 0 auto;
max-width: 550px;
}
.test-area {
margin-bottom: 4em;
padding: 0 2em;
}
.test-wrapper {
border: 10px solid grey;
border-radius: 10px;
}
#origin-text {
margin: 1em 0;
padding: 1em 1em 0;
background-color: #ededed;
}
#origin-text p {
margin: 0;
padding-bottom: 1em;
}
.test-wrapper {
display: flex;
}
.test-wrapper textarea {
flex: 1;
}
.meta {
margin-top: 1em;
display: flex;
justify-content: space-between;
flex-wrap: wrap;
}
.timer {
font-size: 3em;
font-weight: bold;
}
#reset {
padding: .5em 1em;
font-size: 1.2em;
font-weight: bold;
color: #E95D0F;
background: white ;
border: 10px solid #E95D0F;
}
#reset:hover {
color: white;
background-color: #E95D0F;
}
<header class="masthead">
<h1>Test Your Typing Speed</h1>
</header>
<main class="main">
<article class="intro">
<p>This is a typing test. Your goal is to duplicate the provided text, EXACTLY, in the field below. The timer starts when you start typing, and only stops when you match this text exactly. Good Luck!</p>
</article><!-- .intro -->
<section class="test-area">
<div id="origin-text">
<p>The text to test.</p>
</div><!-- #origin-text -->
<div class="test-wrapper">
<textarea id="test-area" name="textarea" rows="6" placeholder="The clock starts when you start typing."></textarea>
</div><!-- .test-wrapper -->
<div class="meta">
<section id="clock">
<div class="timer">00:00:00</div>
</section>
<button id="reset">Start over</button>
</div><!-- .meta -->
</section><!-- .test-area -->
</main>
If efficiency is your question, I would say to use an MVVM framework such as KnockoutJS or Angular, then it's really easy to determine differences and keeps the code real clean.
I'd split the text to copy into two, side by side:
1. Text typed | 2. Text to copy
With a separate box to type into.
You would subtract what you type upon key press, probably bassed on string length, from 2.text to copy, and put it into 1.text typed.
Use a calculated method to determine if it matched, and set the class on 1.text typed accordingly.

How can I add spin buttons inside input type="input"

I want to add <div> tags inside input type="input" for a number-only input field. I know it can't be done, but I was wondering if there was some way to add the + and - buttons inside an input field that isn't input type="number". For reasons that I will not get into (it took me a good two days to solve an issue), I am unable to use input type="number".
How can I make custom spin buttons for an input that doesn't come with spin buttons? Most of the other questions on SO are asking about styling/hiding the spin buttons in a number input.
I suggest you to make a big div containing on a side the input number and, on the other side, make another div which contains the two buttons one on the top of the other.
Since you can't use the type="number" for some reason, here's a small custom stepper.
var numberSteppers = document.querySelectorAll('.numberStepper input');
for(var i = 0; i < numberSteppers.length; i++){
numberSteppers[i].oninput = function(){
this.value = !isNaN(this.value) ? parseInt(this.value) : 0;
}
}
var stepperButtons = document.querySelectorAll('.numberStepper button');
for(var j = 0; j < stepperButtons.length; j++){
stepperButtons[j].onclick = function(e){
e.preventDefault();
var input = this.parentNode.previousElementSibling;
input.value = input.value !== '' ? parseInt(input.value) + parseInt(this.value) : parseInt(this.value);
}
}
.numberStepper, .numberStepper *{
box-sizing:border-box;
}
.numberStepper{
max-width:200px;
position:relative;
}
.numberStepper input{
display:block;
width:80%;
font-size:2em;
min-height:3ex;
padding:1ch;
text-align:right;
}
.numberStepper .steppers{
position:absolute;
right:0;
top:0;
height:100%;
width:20%;
}
.numberStepper button{
margin:0;
padding:0;
border:1px solid;
width:100%;
cursor:pointer;
height:50%;
}
<div class="numberStepper">
<input type="text"/>
<div class="steppers">
<button value="1">+</button>
<button value="-1">-</button>
</div>
</div>
If for some reason you don't want to add divs or change input type to number, you can use this solution.
jQuery('<div class="quantity-nav"><div class="quantity-button quantity-up">+</div><div class="quantity-button quantity-down">-</div></div>').insertAfter('.quantity input');
jQuery('.quantity').each(function() {
var spinner = jQuery(this),
input = spinner.find('input[type="text"]'),
btnUp = spinner.find('.quantity-up'),
btnDown = spinner.find('.quantity-down'),
min = input.attr('min'),
max = input.attr('max');
btnUp.click(function() {
var oldValue = parseFloat(input.val());
if (oldValue >= max) {
var newVal = oldValue;
} else {
var newVal = oldValue + 1;
}
spinner.find("input").val(newVal);
spinner.find("input").trigger("change");
});
btnDown.click(function() {
var oldValue = parseFloat(input.val());
if (oldValue <= min) {
var newVal = oldValue;
} else {
var newVal = oldValue - 1;
}
spinner.find("input").val(newVal);
spinner.find("input").trigger("change");
});
});
.quantity {
position: relative;
}
input[type=text]::-webkit-inner-spin-button,
input[type=text]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type=text] {
-moz-appearance: textfield;
}
.quantity input {
width: 45px;
height: 42px;
line-height: 1.65;
float: left;
display: block;
padding: 0;
margin: 0;
padding-left: 20px;
border: 1px solid #eee;
}
.quantity input:focus {
outline: 0;
}
.quantity-nav {
float: left;
position: relative;
height: 42px;
}
.quantity-button {
position: relative;
cursor: pointer;
border-left: 1px solid #eee;
width: 20px;
text-align: center;
color: #333;
font-size: 13px;
font-family: "Trebuchet MS", Helvetica, sans-serif !important;
line-height: 1.7;
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.quantity-button.quantity-up {
position: absolute;
height: 50%;
top: 0;
border-bottom: 1px solid #eee;
}
.quantity-button.quantity-down {
position: absolute;
bottom: -1px;
height: 50%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="quantity">
<input type="text" value="1">
</div>
This solution seems to work on Chrome and Edge. (Not tested on other browsers).
Credits to this.

Categories