I am making a typing program. Each letter is in its own div, which is broken into words.
For example, the word other would be written as:
<div class="word">
<div class="letter" id="l184"></div>
<div class="letter" id="l185">o</div>
<div class="letter" id="l186">t</div>
<div class="letter" id="l187">h</div>
<div class="letter" id="l188">e</div>
<div class="letter" id="l189">r</div>
</div>
The letter with the cursor before it also has the class cursor.
.cursor:before {
position: absolute;
width: 2px;
height: 30px;
background: var(--accent);
content: ' ';
}
Sometimes, when typing the first word of a line, the completed part of the word, which has the cursor after it, is bumped up to the line before it. This does not happen when the :before has no content. Please help me figure out why something with position: absolute is moving elements around. Thank you!
EDIT: Snippet
EDIT 2: The glitch only works with a certain combination of words, so if you cannot reproduce the glitch, please try again.
let dict = ['test', 'stack', 'overflow'];
let index = 0, words, wrong = 0, last;
let running;
let sec = 0, min = 0;
const text = document.querySelector('#text');
const st = document.querySelector('#sec');
const mt = document.querySelector('#min');
function genTest() {
words = "";
for (let i = 0; i < 100; i++) {
words += dict[randomRange(0, 2)];
if (i !== 99) words += " ";
}
let html = `<div class='word'>`;
let i = 0;
words.split('').forEach(l => {
if (l === ' ') html += `</div><div class="word">`;
if (i === 0) html += `<div class="letter curs-fade" id='l${i}'>${l}</div>`;
else html += `<div class="letter" id='l${i}'>${l}</div>`;
i++;
});
last = i;
html += '</div>'
text.innerHTML = html;
}
function initTest() {
running = false;
genTest();
st.classList = "";
mt.classList = "";
l(0).classList.add('cursor');
}
function start() {
running = true;
st.classList.add('txt-active');
setInterval(() => {
sec++;
if (sec >= 60) {
sec %= 60;
min++;
if (min === 1) {
mt.classList.add('txt-active');
}
}
mt.innerHTML = min.toString() + ':';
st.innerHTML = ((sec < 10) ? '0' : '') + sec.toString();
}, 1000);
}
document.addEventListener('keydown', (e) => {
let key = e.key;
const cl = l(index);
if (wrong > 0 && key === 'Backspace') {
wrong--;
w(wrong).remove();
return;
}
if (key.match(/^[a-zA-Z"'\s]+$/) && key.length === 1) {
if (index === 0 && !running) {
start();
}
if (cl.innerHTML === key.toLowerCase() && wrong === 0) {
cl.classList.add('correct');
index++;
} else if (key !== ' ') {
let w = document.createElement('DIV');
w.classList.add('letter');
w.classList.add('incorrect');
w.id = "w" + wrong;
w.innerHTML = key;
wrong++;
if (index > 0) l(index - 1).appendChild(w);
else {
let n = l(index);
n.parentNode.insertBefore(w, n);
}
}
cl.classList.remove('cursor');
l(index).classList.add('cursor');
}
});
function l(index) {
return document.getElementById('l' + index);
}
function w(index) {
return document.getElementById('w' + index);
}
function randomRange(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
initTest();
#import url('https://fonts.googleapis.com/css2?family=Roboto+Mono:ital,wght#0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&display=swap');
* {
font-family: 'Roboto Mono', monospace;
}
body, html {
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
body {
background-color: #0f0f0f;
--accent: yellow;
--gray: #ababab;
--dark-gray: #8f8f8f;
user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
}
.header {
margin: 10px 150px;
}
.title {
font-size: 50px;
font-weight: 200;
color: white;
}
#text {
color: var(--gray);
margin: 20px 150px;
text-align: left;
}
.timer {
color: var(--dark-gray);
font-size: 50px;
font-weight: 300;
text-align: center;
margin: 100px 0 0;
}
.txt-active {
color: white;
transition: color .7s ease;
}
#min, #sec {
display: inline;
}
.word {
display: inline;
}
.letter {
display: inline;
font-weight: 200;
font-size: 24px;
}
.correct {
color: white;
}
.incorrect {
color: #d26f6f;
}
.cursor:before {
position: absolute;
width: 2px;
height: 30px;
background: var(--accent);
content: ' ';
}
.curs-fade:before {
animation: cursor-fade alternate-reverse .8s infinite;
}
#keyframes cursor-fade {
80% {opacity: 1}
0% {opacity: 0}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="styles/style.css">
<title>Typeracer++</title>
</head>
<body>
<div class="header">
<div class="title">typeracer++</div>
</div>
<div class="test">
<div class="timer"><div id="min">0:</div><div id="sec">00</div></div>
<div id="text"></div>
</div>
<script type="text/javascript" src="scripts/game.js"></script>
</body>
</html>
Related
var grid = document.getElementById('grid');
var msg = document.querySelector('.message');
var chooser = document.querySelector('form');
var mark;
var cells;
// add click listener to radio buttons
function setPlayer() {
mark = this.value;
msg.textContent = mark + ', click on a square to make your move!';
chooser.classList.add('game-on');
this.checked = false;
buildGrid();
}
// add click listener to each cell
function playerMove() {
if (this.textContent == '') {
this.textContent = mark;
checkRow();
switchMark();
computerMove();
}
}
// let the computer make the next move
function computerMove() {
var emptyCells = [];
var random;
/* for (var i = 0; i < cells.length; i++) {
if (cells[i].textContent == '') {
emptyCells.push(cells[i]);
}
}*/
cells.forEach(function(cell){
if (cell.textContent == '') {
emptyCells.push(cell);
}
});
// computer marks a random EMPTY cell
random = Math.ceil(Math.random() * emptyCells.length) - 1;
emptyCells[random].textContent = mark;
checkRow();
switchMark();
}
// switch player mark
function switchMark() {
if (mark == 'X') {
mark = 'O';
} else {
mark = 'X';
}
}
// determine a winner
function winner(a, b, c) {
if (a.textContent == mark && b.textContent == mark && c.textContent == mark) {
msg.textContent = mark + ' is the winner!';
a.classList.add('winner');
b.classList.add('winner');
c.classList.add('winner');
return true;
} else {
return false;
}
}
// check cell combinations
function checkRow() {
winner(document.getElementById('c1'), document.getElementById('c2'), document.getElementById('c3'));
winner(document.getElementById('c4'), document.getElementById('c5'), document.getElementById('c6'));
winner(document.getElementById('c7'), document.getElementById('c8'), document.getElementById('c9'));
winner(document.getElementById('c1'), document.getElementById('c4'), document.getElementById('c7'));
winner(document.getElementById('c2'), document.getElementById('c5'), document.getElementById('c8'));
winner(document.getElementById('c3'), document.getElementById('c6'), document.getElementById('c9'));
winner(document.getElementById('c1'), document.getElementById('c5'), document.getElementById('c9'));
winner(document.getElementById('c3'), document.getElementById('c5'), document.getElementById('c7'));
}
// clear the grid
function resetGrid() {
mark = 'X';
/* for (var i = 0; i < cells.length; i++) {
cells[i].textContent = '';
cells[i].classList.remove('winner');
}*/
cells.forEach(function(cell){
cell.textContent = '';
cell.classList.remove('winner');
});
msg.textContent = 'Choose your player:';
chooser.classList.remove('game-on');
grid.innerHTML = '';
}
// build the grid
function buildGrid() {
for (var i = 1; i <= 9; i++) {
var cell = document.createElement('li');
cell.id = 'c' + i;
cell.addEventListener('click', playerMove, false);
grid.appendChild(cell);
}
/* cells = document.querySelectorAll('li'); //Returns a NodeList, not an Array
See https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches */
cells = Array.prototype.slice.call(grid.getElementsByTagName('li'));
}
var players = Array.prototype.slice.call(document.querySelectorAll('input[name=player-choice]'));
players.forEach(function(choice){
choice.addEventListener('click', setPlayer, false);
});
var resetButton = chooser.querySelector('button');
resetButton.addEventListener('click', function(e) {
e.preventDefault();
resetGrid();
});
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
background-color: #996E89;
font-family: "Comfortaa", sans-serif;
}
h1 {
font-family: "Lobster", serif;
}
.message {
color: #fff;
font-size: 1.5em;
padding-bottom: 1em;
}
form {
label {
font-size: 4em;
font-weight: bold;
vertical-align: middle;
}
input[type=radio] {
margin: 1em;
cursor: pointer;
}
fieldset {
display: block;
opacity: 1;
transition: all ease 1s;
}
&.game-on fieldset {
opacity: 0;
display: none;
}
&.game-on button {
opacity: 1;
display: block;
margin: 0 auto;
}
}
#grid-section {
margin: 3em 0;
}
#grid {
width: 300px;
margin: 0 auto;
}
li {
border: 1px solid #000;
width: 100px;
height: 100px;
display: block;
float: left;
font-size: 3em;
text-align: center;
padding: .5em;
}
#c1, #c2, #c3 {
border-top: none;
}
#c3, #c6, #c9 {
border-right: none;
}
#c7, #c8, #c9 {
border-bottom: none;
}
#c1, #c4, #c7 {
border-left: none;
}
.winner {
color: #9AE1E5;
}
.btn-primary,
.btn-primary:focus {
background-color: #33B7A5;
opacity: 0;
outline: 0;
transition: all ease .3s;
}
.btn-primary:hover {
background-color: #4C2F39;
}
<div class="container">
<div class="row text-center" id="intro-section">
<h1>Player vs Computer Tic-Tac-Toe</h1>
<h2 class="message">Choose your player:</h2>
<form action="#">
<fieldset>
<input type="radio" name="player-choice" id="player-choice-1" value="X" />
<label for="player-choice-1">X</label>
<input type="radio" name="player-choice" id="player-choice-2" value="O" />
<label for="player-choice-2">O</label>
</fieldset>
<button id="reset" class="btn btn-primary">Reset</button>
</form>
</div>
<div class="row" id="grid-section">
<ul id="grid"></ul>
</div>
I wish to add a scoreboard on top where wins,loses,draws would be calculated. How do I go about the same? Also an another HTML page where I could see the number of wins, loses, draws.
I have tried adding table in the html and using it in JS but not able to implement the same.
Could you suggest any methods to implement these tasks in the project.
I am trying to make a simple calculator and am almost done with it except I cannot fix its screen height. When I enter the data, the height of the calculator screen changes. To reproduce the problem, you can try adding 1 + 2. I have been attempting adding min-height and max-height but to no avail. I can fix its height with js, but I want a pure CSS solution. Here is the code:
let screen = document.getElementById("screen");
let firstArg = document.querySelector(".firstArg");
let operator = document.querySelector(".operator");
let secondArg = document.querySelector(".secondArg");
let numberBtns = document.querySelectorAll(".data-number");
let acBtn = document.querySelector(".ac");
let clearBtn = document.querySelector(".clear");
let backBtn = document.querySelector(".back");
let decimalBtn = document.querySelector(".decimal");
let equalBtn = document.querySelector(".equal");
let operationBtn = document.querySelectorAll(".operation");
let regex = /[+-/*]/;
let digits = /[0-9]/;
numberBtns.forEach(n => n.addEventListener("click", e => {
if (firstArg.innerHTML === "0") {
firstArg.innerHTML = "";
}
if (operator.textContent === "" && firstArg.clientWidth + 40 < screen.offsetWidth) {
firstArg.append(n.textContent);
} else if (operator.textContent && secondArg.clientWidth + 40 < screen.offsetWidth) {
secondArg.append(n.textContent);
}
}));
decimalBtn.addEventListener("click", e => {
if (!firstArg.textContent.includes(".") && operator.textContent === "") {
firstArg.append(decimalBtn.textContent);
} else if (!secondArg.textContent.includes(".") && operator.textContent) {
secondArg.append(decimalBtn.textContent);
}
});
operationBtn.forEach(n => n.addEventListener("click", e => {
if (!regex.test(operator.textContent)) {
operator.append(n.textContent);
firstArg.append(" ");
firstArg.append(operator.textContent);
}
}))
equalBtn.addEventListener("click", e => {
let firstParam = firstArg.textContent.split(" ")[0];
let oper = operator.textContent;
let secondParam = secondArg.textContent;
if (secondParam == "") return;
solve(firstParam, oper, secondParam);
})
document.addEventListener("keydown", e => {
if (e.key == "+") {
if (!regex.test(operator.textContent)) {
operator.append(e.key);
firstArg.append(" ");
firstArg.append(operator.textContent);
}
}
if (e.key == "/") {
if (!regex.test(operator.textContent)) {
operator.append(e.key);
firstArg.append(" ");
firstArg.append(operator.textContent);
}
}
if (e.key == "-") {
if (!regex.test(operator.textContent)) {
operator.append(e.key);
firstArg.append(" ");
firstArg.append(operator.textContent);
}
}
if (e.key == "*") {
if (!regex.test(operator.textContent)) {
operator.append(e.key);
firstArg.append(" ");
firstArg.append(operator.textContent);
}
}
if (digits.test(e.key)) {
if (firstArg.innerHTML === "0") {
firstArg.innerHTML = "";
}
if (operator.textContent === "" && firstArg.clientWidth + 40 < screen.offsetWidth) {
firstArg.append(e.key);
} else if (operator.textContent && secondArg.clientWidth + 40 < screen.offsetWidth) {
secondArg.append(e.key);
}
}
if (e.code === "Enter") {
let firstParam = firstArg.textContent.split(" ")[0];
let oper = operator.textContent;
let secondParam = secondArg.textContent;
if (secondParam == "") return;
solve(firstParam, oper, secondParam);
}
})
function solve(first, oper, second) {
switch (oper) {
case "-":
firstArg.innerHTML = `${((Number(first) * 10 - Number(second) * 10) / 10).toFixed(1)}`;
operator.textContent = "";
secondArg.textContent = "";
break;
case "+":
firstArg.innerHTML = `${((Number(first) * 10 + Number(second) * 10) / 10).toFixed(1)}`;
operator.textContent = "";
secondArg.textContent = "";
break;
case "/":
firstArg.innerHTML = `${((Number(first) * 10 / Number(second) * 10) / 10).toFixed(1)}`;
operator.textContent = "";
secondArg.textContent = "";
break;
case "*":
firstArg.innerHTML = `${((Number(first) * 10 * Number(second) * 10) / 10).toFixed(1)}`;
operator.textContent = "";
secondArg.textContent = "";
break;
}
}
backBtn.addEventListener("click", e => {
if ((operator.textContent || firstArg.textContent.length > 1) && secondArg.textContent == "") {
operator.innerHTML = "";
let remaining = firstArg.textContent.slice(0, firstArg.textContent.length - 1).trim();
firstArg.innerHTML = "";
firstArg.innerHTML = remaining;
} else if (firstArg.textContent.length === 1 && secondArg.textContent == "") {
firstArg.innerHTML = "0";
} else if (secondArg.textContent) {
let remaining = secondArg.textContent.slice(0, secondArg.textContent.length - 1).trim();
secondArg.innerHTML = "";
secondArg.innerHTML = remaining;
}
})
window.onload = function() {
firstArg.innerHTML = 0;
}
acBtn.addEventListener("click", function() {
firstArg.innerHTML = 0;
secondArg.innerHTML = "";
operator.textContent = "";
})
.claculator {
display: grid;
grid-template-rows: 70px 1fr;
grid-gap: 35px 0;
margin: 0 auto;
border: 2px solid black;
width: 350px;
height: 370px;
border-radius: 27px;
padding: 12px 9px;
align-items: center;
background-color: #191a1c;
}
#screen {
background-color: #ffffff;
border-radius: 5px;
min-height: 70px;
min-width: 200px;
margin-top: 20px;
}
#funBtns {
display: grid;
grid-template-columns: repeat(4, 50px);
grid-template-rows: repeat(5, 30px);
grid-gap: 20px 40px;
justify-content: center;
}
button {
background-color: #191a1c;
color: white;
border: none;
border: 1px solid white;
border-radius: 5px;
width: 100%;
}
button:hover {
transform: translateY(-5px);
transition: 200ms ease-out;
background-color: white;
color: black;
}
.button-plus {
grid-row: span 2;
}
#screen {
display: flex;
flex-direction: column;
align-items: flex-end;
row-gap: 25px;
padding-top: 10px;
padding-right: 10px;
}
.firstArg {
float: right;
font-size: 20px;
font-weight: 500;
color: rgb(0, 0, 0);
text-overflow: clip;
}
.secondArg {
float: right;
font-size: 25px;
font-weight: 900;
color: rgb(24, 22, 22);
}
.operator {
display: none;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Calculator</title>
<link rel="stylesheet" href="style.css">
<script src="calculator.js" defer></script>
</head>
<body>
<div class="claculator">
<div id="screen">
<span class="firstArg"></span>
<span class="operator"></span>
<span class="secondArg"></span>
</div>
<div id="funBtns">
<button class="button ac">AC</button>
<button class="button clear">Clear</button>
<button class="button back">Back</button>
<button class="operation">/</button>
<button class="data-number">1</button>
<button class="data-number">2</button>
<button class="data-number">3</button>
<button class="operation">*</button>
<button class="data-number">4</button>
<button class="data-number">5</button>
<button class="data-number">6</button>
<button class="operation">-</button>
<button class="data-number">7</button>
<button class="data-number">8</button>
<button class="data-number">9</button>
<button class="operation button-plus">+</button>
<button class="button decimal">.</button>
<button class="data-number">0</button>
<button class="button equal">=</button>
</div>
</div>
</body>
</html>
change the font size to 1 rem
.secondArg {
float: right;
font-size: 1rem;
font-weight: 900;
color: rgb(24, 22, 22)}
Make the font-size for #screen equal to 1rem
then height to 1rem(if you want screen to be one line high) or 2rem(for two line high)
1rem = :root font-size in pixel.
Since you have added padding(and apparently there are two #screen), add padding height in terms of rem to height as well.
Total screen height= height(in rem) + padding(in rem)
Link for rem to pixel conversion: http://www.standardista.com/px-to-rem-conversion-if-root-font-size-is-16px/.
Try setting the css value for max-height to the div's class or id.
Here is a beautiful Number Ticker. the whole day I was wondering and trying to modify the code to make it as I want but no success till now!
if you work with numbers with two or more digits then the code creates separate black squares to hold each digit ( run code snippet to have a look ), but I want only a single square as the container to hold multiple digit numbers. So if we have a two-digit number like 10 the Number Ticker should be something like this:
And the next move should look like :
I don't want those parallel animations that move two digits like this (Only the single animation is required not both):
Here is the code:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
let currentValue = 10;
let digits = [];
generateDigits(currentValue.toString().length);
setValue(currentValue);
setTimeout(function() {
setValue(8);
}, 2000);
setTimeout(function() {
setValue(7);
}, 5000);
function setValue(number) {
let s = number.toString().split('').reverse().join('');
let l = s.length;
if (l > digits.length) {
generateDigits(l - digits.length);
}
for (let i = 0; i < digits.length; i++) {
setDigit(i, s[i] || 0);
}
}
function setDigit(digitIndex, number) {
digits[digitIndex].style.marginTop = '-' + number + 'em';
}
function generateDigits(amount) {
for (let i = 0; i < amount; i++) {
let d = defaultDigitNode.cloneNode(true);
counter.appendChild(d);
digits.unshift(d);
}
}
});
:root {
background-color: #555;
color: white;
font-size: 25vh;
font-family: Roboto Light;
}
body,
html {
margin: 0;
height: 100%;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.number-ticker {
overflow: hidden;
height: 1em;
background-color: #333;
box-shadow: 0 0 0.05em black inset;
}
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Number Ticker</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="number-ticker.css">
</head>
<body>
<div class="container">
<div class="number-ticker" data-value="0"></div>
</div>
<script src="number-ticker.js"></script>
</body>
</html>
Your css has this
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
}
You need to change it to this
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
padding: 0 0.075em;
text-align: center;
}
If you remove border-right: 1px solid #555 you will have it look like 1 box.
Also I added text-align: center to center the numbers.
Hope this solves your problem :)
I think the main issue in your code is the digits variable. It creates an array of HTML elements that holds two blocks.
Also, for this line:
let s = number.toString().split('').reverse().join('');
Why do you need to convert number to a string. You can just pass it as is. Once you add to a string using + it will be converted.
I made few changes to your code and commented out the non-relevant part. Please see below:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
// let currentValue = 10;
// let digits = [];
let currentValue = counter.getAttribute("data-value");
let digit = null;
generateDigits(currentValue.toString().length);
setValue(currentValue);
setTimeout(function() {
setValue(8);
}, 2000);
setTimeout(function() {
setValue(7);
}, 5000);
setTimeout(function() {
setValue(10);
}, 8000);
function setValue(number) {
// let s = number.toString().split('').reverse().join('');
// let l = s.length;
/*if (l > digits.length) {
generateDigits(l - digits.length);
}*/
/*for (let i = 0; i < digits.length; i++) {
setDigit(i, s[i] || 0);
}*/
digit.style.marginTop = '-' + number + 'em';
}
/*function setDigit(digitIndex, number) {
console.log(number);
digits[digitIndex].style.marginTop = '-' + number + 'em';
}*/
function generateDigits(amount) {
// console.log("generat", amount);
// for (let i = 0; i < amount; i++) {
let d = defaultDigitNode.cloneNode(true);
digit = counter.appendChild(d);
// digits.unshift(d);
// }
}
});
:root {
background-color: #555;
color: white;
font-size: 25vh;
font-family: Roboto Light;
}
body,
html {
margin: 0;
height: 100%;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.number-ticker {
overflow: hidden;
height: 1em;
background-color: #333;
box-shadow: 0 0 0.05em black inset;
}
.number-ticker .digit {
float: left;
line-height: 1;
transition: margin-top 1.75s ease;
border-right: 1px solid #555;
padding: 0 0.075em;
text-align: center;
}
<div class="container">
<div class="number-ticker" data-value="0"></div>
</div>
Your final JS could be like this:
let counters = document.getElementsByClassName('number-ticker');
let defaultDigitNode = document.createElement('div');
defaultDigitNode.classList.add('digit');
for (let i = 0; i < 11; i++) {
defaultDigitNode.innerHTML += i + '<br>';
}
[].forEach.call(counters, function(counter) {
let currentValue = counter.getAttribute("data-value");
let d = defaultDigitNode.cloneNode(true);
let digit = counter.appendChild(d);
setValue(currentValue);
function setValue(number) {
digit.style.marginTop = '-' + number + 'em';
}
});
https://codepen.io/anon/pen/EOrRXo
This is my code so far. It just uses JS to generate a bunch of styled divs. I don't know how to input a picture instead of a string to fill in my divs. They all have values that I add up to generate each players' score to use bitwise "&" calculation to determine when someone has won. There's a play again button that works but it's broken for you because the image is local. Also it's extreme so it's spinning and I'm sorry if that annoys you.
HTML:
<!DOCTYPE html>
<html>
<head>
<title>EXTREME TIC TAC TOE</title>
<link type="text/css" rel="stylesheet" href="css/style.css">
<script src="js/script.js"></script>
</head>
<body onload = "startGame();">
<h2 id="game-message"> Tic Tac Toe </h2>
<div id="game-board">
</div>
<div id="restartButton" >
<img src="img/lets-play.gif" id="button" onclick="restartGame();">
</div>
</body>
</html>
CSS
/* CSS */
* {margin: 0; padding: 0; user-select: none; text-transform: uppercase;}
body {background-image: url('../img/name.type'); }
h2#game-message
{
font-size: 60px;
font-family: Tahoma;
margin-bottom: 15px;
text-align: center;
}
div#game-board
{
overflow: hidden;
margin: 20px auto;
width:870px;
}
div[id^="row-"] {clear: both;}
div [id^="row-"] div
{
//border: 30px solid plum;
height: 270px;
width: 270px;
float: left;
text-align: center;
font-family: Tahoma;
font-size: 175px;
border-width: 15px;
border-style: solid;
border-image: url('../img/border.jpg') 25% repeat;
}
div#row-1 div {border-top: none;}
div#row-3 div {border-bottom: none;}
div[id^="row-"] div:first-child {border-left: none}
div[id^="row-"] div:last-child {border-right: none}
#button
{
color: white;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
border-width: 5px;
border-style: solid;
border-image: url('../img/borderr.png') 25% round;
}
#restartButton
{
position: absolute;
left: 880px;
top: 1075px;
}
#keyframes rotation
{
from {
transform: rotate(0deg);
}
to {
transform: rotate(359deg);
}
}
#keyframes slide {
0% {left: 0;}
100% { left: 1500px;}
}
#game-board
{
position: relative;
}
JS
//JAVASCRIPT
var markers = ["X", "O"];
//var players = [];
var players = ["Max", "Dennis"];
var totals = [];
var winCodes = [7,56,73,84,146,273,292,448];
var whoseTurn = 0;
var gameOver = false;
var speed = [2, 2];
// Play again button
function restartGame()
{
startGame();
}
function startGame() // Choose your names, and display them in the header message.
{
// makes board spin initially
document.getElementById("game-board").style.animation = "rotation 4s infinite linear";
//players[0] = prompt("Player 1 NAME:");
//players[1] = prompt("Player 2 NAME:");
var counter = 1;
var innerDivs = "";
for (i = 1; i <=3; i++)
{
innerDivs += '<div id="row-' + i + '">';
for (j = 1; j <= 3; j++)
{
innerDivs += '<div onclick="playGame(this,' + counter + ');"></div>';
counter *= 2;
}
innerDivs += '</div>';
}
document.getElementById("game-board").innerHTML = innerDivs;
totals = [0, 0];
gameOver = false;
document.getElementById("game-message").innerText = "IT'S YOUR TURN " + players[whoseTurn];
}
function playGame(clickedDiv, divValue)
{
if (!gameOver)
{
// changes speed depending on how many turns each player has done
speed[whoseTurn]++;
document.getElementById("game-board").style.animation = "rotation "+ 8 / speed[whoseTurn] + "s infinite linear";
// Adds X or O depending on whoseTurn
clickedDiv.innerText = markers[whoseTurn];
// adds up total each time a player "moves" to track a win condition
totals[whoseTurn] += divValue;
// calls isWin() function to see if someone won
if (isWin())
{
document.getElementById("game-message").innerText = "WOW VERY COOL " + players[whoseTurn] + " YOU WON";
document.getElementById("game-board").style.animation = "slide 2s forwards, rotation "+ 8 / speed[whoseTurn] + "s infinite linear";
}
else if (gameOver)
{
document.getElementById("game-message").innerText = "YOU BOTH FAILED";
}
else
{
// Switches turn each click
if (whoseTurn) whoseTurn = 0; else whoseTurn = 1;
// disables onclick tag after clicked so it cannot be used >1 times.
clickedDiv.attributes["0"].nodeValue = "";
// Displays text for which turn it is
document.getElementById("game-message").innerText = "IT'S YOUR TURN " + players[whoseTurn];
}
}
}
// win code logic
function isWin()
{
for (i = 0; i < winCodes.length; i++)
{
if ((totals[whoseTurn] & winCodes[i]) == winCodes[i]) {gameOver = true; return true;}
}
if (totals[0] + totals[1] == 511) {gameOver = true;}
return false;
}
You can start by replacing the markers array with (or creating a new array, such as markerImages) image elements.
Then replace clickedDiv.innerText with clickedDiv.innerHTML.
when I open my website (https://scampsblog.com/code/openjarvis/two/index.html) a div shows up with which the user can interact (e.g. type "help"). My problem is, that when somebody opens the website on a computer, they can type as expected, but opening the website via a smartphone will prevent them from any interaction. Using contenteditable solved the smartphone issue but resulted in some weird bugs.
Here is the code I have so far:
/*
// Made with <3 by Marcus Bizal
// github.com/marcbizal
// linkedin.com/in/marcbizal
*/
$(document).ready(function() {
"use strict";
// UTILITY
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
// END UTILITY
// COMMANDS
function clear() {
terminal.text("");
}
function help() {
terminal.append("There is no help... MUAHAHAHAHA. >:D\n");
}
function echo(args) {
var str = args.join(" ");
terminal.append(str + "\n");
}
function fortune() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://cdn.rawgit.com/bmc/fortunes/master/fortunes', false);
xhr.send(null);
if (xhr.status === 200) {
var fortunes = xhr.responseText.split("%");
var fortune = fortunes[getRandomInt(0, fortunes.length)].trim();
terminal.append(fortune + "\n");
}
}
// END COMMANDS
var title = $(".title");
var terminal = $(".terminal");
var prompt = "";
var path = ">";
var commandHistory = [];
var historyIndex = 0;
var command = "";
var commands = [{
"name": "clear",
"function": clear
}, {
"name": "help",
"function": help
}, {
"name": "fortune",
"function": fortune
}, {
"name": "echo",
"function": echo
}];
function processCommand() {
var isValid = false;
// Create args list by splitting the command
// by space characters and then shift off the
// actual command.
var args = command.split(" ");
var cmd = args[0];
args.shift();
// Iterate through the available commands to find a match.
// Then call that command and pass in any arguments.
for (var i = 0; i < commands.length; i++) {
if (cmd === commands[i].name) {
commands[i].function(args);
isValid = true;
break;
}
}
// No match was found...
if (!isValid) {
terminal.append("openjarvis: command not found: " + command + "\n");
}
// Add to command history and clean up.
commandHistory.push(command);
historyIndex = commandHistory.length;
command = "";
}
function displayPrompt() {
terminal.append("<span class=\"prompt\">" + prompt + "</span> ");
terminal.append("<span class=\"path\">" + path + "</span> ");
}
// Delete n number of characters from the end of our output
function erase(n) {
command = command.slice(0, -n);
terminal.html(terminal.html().slice(0, -n));
}
function clearCommand() {
if (command.length > 0) {
erase(command.length);
}
}
function appendCommand(str) {
terminal.append(str);
command += str;
}
/*
// Keypress doesn't catch special keys,
// so we catch the backspace here and
// prevent it from navigating to the previous
// page. We also handle arrow keys for command history.
*/
$(document).keydown(function(e) {
e = e || window.event;
var keyCode = typeof e.which === "number" ? e.which : e.keyCode;
// BACKSPACE
if (keyCode === 8 && e.target.tagName !== "INPUT" && e.target.tagName !== "TEXTAREA") {
e.preventDefault();
if (command !== "") {
erase(1);
}
}
// UP or DOWN
if (keyCode === 38 || keyCode === 40) {
// Move up or down the history
if (keyCode === 38) {
// UP
historyIndex--;
if (historyIndex < 0) {
historyIndex++;
}
} else if (keyCode === 40) {
// DOWN
historyIndex++;
if (historyIndex > commandHistory.length - 1) {
historyIndex--;
}
}
// Get command
var cmd = commandHistory[historyIndex];
if (cmd !== undefined) {
clearCommand();
appendCommand(cmd);
}
}
});
$(document).keypress(function(e) {
// Make sure we get the right event
e = e || window.event;
var keyCode = typeof e.which === "number" ? e.which : e.keyCode;
// Which key was pressed?
switch (keyCode) {
// ENTER
case 13:
{
terminal.append("\n");
processCommand();
displayPrompt();
break;
}
default:
{
appendCommand(String.fromCharCode(keyCode));
}
}
});
// Set the window title
title.text("openjarvis setup");
// Get the date for our fake last-login
var date = ("setup");
// Display last-login and prompt
terminal.append("openjarvis " + date + " \n");
displayPrompt();
});
html,
body {
height: 100%;
overflow: hidden;
}
body {
display: flex;
justify-content: center;
align-items: center;
}
* {
box-sizing: border-box;
}
textarea,
input,
button {
outline: none;
}
.window-button,
.window .buttons .close,
.window .buttons .minimize,
.window .buttons .maximize {
padding: 0;
margin: 0;
margin-right: 4px;
width: 12px;
height: 12px;
background-color: gainsboro;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 6px;
color: rgba(0, 0, 0, 0.5);
}
.window {
animation: bounceIn 1s ease-in-out;
width: 640px;
}
.window .handle {
height: 22px;
background: linear-gradient(0deg, #d8d8d8, #ececec);
border-top: 1px solid white;
border-bottom: 1px solid #b3b3b3;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: rgba(0, 0, 0, 0.7);
font-family: Helvetica, sans-serif;
font-size: 13px;
line-height: 22px;
text-align: center;
}
.window .buttons {
position: absolute;
float: left;
margin: 0 8px;
}
.window .buttons .close {
background-color: #ff6159;
}
.window .buttons .minimize {
background-color: #ffbf2f;
}
.window .buttons .maximize {
background-color: #25cc3e;
}
.window .terminal {
padding: 4px;
background-color: black;
opacity: 0.7;
height: 218px;
color: white;
font-family: 'Source Code Pro', monospace;
font-weight: 200;
font-size: 14px;
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
overflow-y: auto;
}
.window .terminal::after {
content: "|";
animation: blink 2s steps(1) infinite;
}
.prompt {
color: #bde371;
}
.path {
color: #5ed7ff;
}
#keyframes blink {
50% {
color: transparent;
}
}
/*#keyframes bounceIn {
0% {
transform: translateY(-1000px);
}
60% {
transform: translateY(200px);
}
100% {
transform: translateY(0px);
}
}*/
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>openjarvis</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css">
<link rel="stylesheet" href="cmd.css">
</head>
<body>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro:200' rel='stylesheet' type='text/css'>
<div class="container">
<div class="window">
<div class="handle">
<div class="buttons">
<button class="close">
</button>
<button class="minimize">
</button>
<button class="maximize">
</button>
</div>
<span class="title"></span>
</div>
<div class="terminal"></div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js" defer></script>
<script src="cmd.js" defer></script>
</body>
</html>