Add score board in ti-tac-toe game - javascript

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.

Related

how to ignore tilde accent in search filter js?

When I look in the search engine for a word with an accent, for example: Débora, Lázaro, Ángela, Álvaro, Arquímedes, etc. does not find the result.
Without accents or with upper or lower case, the search engine works excellent.
Someone who can help me, I'm still a beginner in programming.
This is my complete code
const deepMerge = (...objects) => {
const isObject = (obj) => obj && typeof obj === "object";
return objects.reduce((prev, obj) => {
Object.keys(obj).forEach((key) => {
const pVal = prev[key];
const oVal = obj[key];
if (Array.isArray(pVal) && Array.isArray(oVal)) {
prev[key] = pVal.concat(...oVal);
} else if (isObject(pVal) && isObject(oVal)) {
prev[key] = deepMerge(pVal, oVal);
} else {
prev[key] = oVal;
}
});
return prev;
}, {});
};
const DEFAULT_OPTIONS = {
classNames: {
wrapperEl: "cselect",
selectEl: "cselect__select",
renderedEl: "cselect__rendered",
renderedTextEl: "cselect__rendered-text",
searchEl: "cselect__search",
optionsEl: "cselect__options",
optionEl: "cselect__option",
init: "js-init-cselect",
open: "is-open",
onTop: "is-on-top",
selected: "is-selected",
hidden: "is-hidden"
},
minimumOptionsForSearch: 10,
onOpen: null,
onClose: null,
onToggle: null
};
class CSelect {
// Elements
#wrapperEl;
#renderedEl;
#renderedTextEl;
#searchEl;
#optionsEl;
#optionEls;
// Functions
#handleSearch;
#optionElClick;
#clickOutside;
#escPress;
constructor(selectEl, options = {}) {
// Handle arguments
this.selectEl = selectEl;
this.options = deepMerge(DEFAULT_OPTIONS, options);
// Bind 'this'
this.open = this.open.bind(this);
this.close = this.close.bind(this);
this.toggle = this.toggle.bind(this);
this.#handleSearch = this.#handleSearchFn.bind(this);
this.#optionElClick = this.#optionElClickFn.bind(this);
this.#clickOutside = this.#clickOutsideFn.bind(this);
this.#escPress = this.#escPressFn.bind(this);
// Functions
this.init();
}
init() {
// Check if already init
if (this.selectEl.classList.contains(this.options.classNames.init)) {
console.error(`CSelect already initialized. ID: ${this.selectEl.id}`);
return;
}
// Handle select element
this.selectEl.setAttribute("tabindex", "-1");
this.selectEl.classList.add(this.options.classNames.selectEl);
// Functions
this.#generateHTML();
this.#addEvents();
// Add initialization
this.selectEl.classList.add(this.options.classNames.init);
}
#generateHTML() {
// Generate wrapper
const wrapperHTML = /* HTML */ `
<div class="${this.options.classNames.wrapperEl}"></div>
`;
this.selectEl.insertAdjacentHTML("beforebegin", wrapperHTML);
this.#wrapperEl = this.selectEl.previousElementSibling;
this.#wrapperEl.appendChild(this.selectEl);
// Generate rendered
const selectedOption = this.selectEl.options[this.selectEl.selectedIndex];
const selectedOptionText = selectedOption.textContent;
this.#renderedEl = document.createElement("button");
this.#renderedEl.type = "button";
this.#renderedEl.className = this.options.classNames.renderedEl;
this.#wrapperEl.appendChild(this.#renderedEl);
this.#renderedTextEl = document.createElement("span");
this.#renderedTextEl.className = this.options.classNames.renderedTextEl;
this.#renderedTextEl.textContent = selectedOptionText;
this.#renderedEl.appendChild(this.#renderedTextEl);
// Generate options wrapper
this.#optionsEl = document.createElement("div");
this.#optionsEl.className = this.options.classNames.optionsEl;
this.#wrapperEl.appendChild(this.#optionsEl);
// Generate search
if (
[...this.selectEl.options].length >= this.options.minimumOptionsForSearch
) {
this.#searchEl = document.createElement("input");
this.#searchEl.type = "text";
this.#searchEl.className = this.options.classNames.searchEl;
this.#optionsEl.appendChild(this.#searchEl);
}
// Generate each option
const selectOptions = [...this.selectEl.options];
this.#optionEls = [];
for (const option of selectOptions) {
if (option.disabled) {
continue;
}
const newOption = document.createElement("button");
newOption.type = "button";
newOption.className = this.options.classNames.optionEl;
newOption.textContent = option.textContent;
newOption.setAttribute("data-value", option.value);
if (option.selected) {
newOption.classList.add(this.options.classNames.selected);
}
this.#optionsEl.appendChild(newOption);
this.#optionEls.push(newOption);
}
}
open(callback) {
this.#wrapperEl.classList.add(this.options.classNames.open);
// Handle optionsEl position
this.#handleOptionsElPosition();
// Handle search
if (this.#searchEl !== null) {
this.#resetSearch();
this.#searchEl.focus();
}
// Handle callback functions
if (typeof this.options.onOpen === "function") {
this.options.onOpen(this);
}
if (typeof callback === "function") {
callback(this);
}
}
close(callback) {
this.#wrapperEl.classList.remove(this.options.classNames.open);
// Handle callback functions
if (typeof this.options.onClose === "function") {
this.options.onClose(this);
}
if (typeof callback === "function") {
callback(this);
}
}
toggle(callback) {
if (!this.#wrapperEl.classList.contains(this.options.classNames.open)) {
this.open();
} else {
this.close();
}
// Handle callback functions
if (typeof this.options.onToggle === "function") {
this.options.onToggle(this);
}
if (typeof callback === "function") {
callback(this);
}
}
#handleOptionsElPosition() {
this.#optionsEl.classList.remove(this.options.classNames.onTop);
const boundingRect = this.#optionsEl.getBoundingClientRect();
const isOutTop = boundingRect.top < 0;
const isOutBottom =
boundingRect.bottom >
(window.innerHeight || document.documentElement.clientHeight);
if (isOutBottom) {
this.#optionsEl.classList.add(this.options.classNames.onTop);
}
if (isOutTop) {
this.#optionsEl.classList.remove(this.options.classNames.onTop);
}
}
#resetSearch() {
this.#searchEl.value = "";
for (const optionEl of this.#optionEls) {
optionEl.classList.remove(this.options.classNames.hidden);
}
}
#handleSearchFn() {
for (const optionEl of this.#optionEls) {
if (
optionEl.textContent
.toLowerCase()
.indexOf(this.#searchEl.value.toLowerCase()) > -1
) {
optionEl.classList.remove(this.options.classNames.hidden);
} else {
optionEl.classList.add(this.options.classNames.hidden);
}
}
}
#optionElClickFn(event) {
// Close the select
this.close();
// Cache the target
const target = event.target;
// Check if click selected option
if (this.selectEl.value === target.dataset.value) {
return;
}
// Handle rendered text
this.#renderedTextEl.textContent = target.textContent;
// Handle select element change
this.selectEl.value = target.dataset.value;
const triggerEvent = new Event("change");
this.selectEl.dispatchEvent(triggerEvent);
// Highlight selected
for (const optionEl of this.#optionEls) {
optionEl.classList.remove(this.options.classNames.selected);
}
target.classList.add(this.options.classNames.selected);
}
#clickOutsideFn(event) {
const isOutside =
event.target.closest(`.${this.options.classNames.wrapperEl}`) !==
this.#wrapperEl;
const isOpen = this.#wrapperEl.classList.contains(
this.options.classNames.open
);
if (isOutside && isOpen) {
this.close();
}
}
#escPressFn(event) {
const isEsc = event.keyCode === 27;
const isOpen = this.#wrapperEl.classList.contains(
this.options.classNames.open
);
if (isEsc && isOpen) {
this.close();
}
}
#addEvents() {
this.#renderedEl.addEventListener("click", this.toggle);
if (this.#searchEl !== null) {
this.#searchEl.addEventListener("input", this.#handleSearch);
}
for (const optionEl of this.#optionEls) {
optionEl.addEventListener("click", this.#optionElClick);
}
document.addEventListener("click", this.#clickOutside);
document.addEventListener("keyup", this.#escPress);
}
destroy() {
// Check if already init
if (!this.selectEl.classList.contains(this.options.classNames.init)) {
console.error(`CSelect not initialized. ID: ${this.selectEl.id}`);
return;
}
// Remove Events
document.removeEventListener("click", this.#clickOutside);
document.removeEventListener("keyup", this.#escPress);
// Unwrap
this.#wrapperEl.replaceWith(this.selectEl);
// Clear select element
this.selectEl.removeAttribute("tabindex");
this.selectEl.classList.remove(this.options.classNames.selectEl);
this.selectEl.classList.remove(this.options.classNames.init);
}
}
// ***
window.addEventListener("DOMContentLoaded", () => {
window["selectObj"] = {};
const selectEls = [...document.querySelectorAll(".js-select")];
for (const selectEl of selectEls) {
window["selectObj"][selectEl.id] = new CSelect(selectEl, {
minimumOptionsForSearch: 0
// onClose: (cselectObj) => {
// console.log(cselectObj)
// },
});
}
// window['selectObj']['select1'].open((cselectObj) => {
// console.log(cselectObj)
// })
});
body {
margin: 0;
padding: 100px 150px;
font-family: sans-serif;
}
/* *** */
.cselect {
position: relative;
}
.cselect,
.cselect *,
.cselect *:before,
.cselect *:after {
box-sizing: border-box;
}
.cselect__select {
display: block;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
z-index: -1;
pointer-events: none;
}
.cselect__rendered {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0;
padding: 6px 12px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #fff;
line-height: 1.5;
text-align: left;
text-decoration: none;
background: #333;
border: 0;
border-radius: 6px;
cursor: pointer;
}
.cselect__rendered:after {
content: "▾";
display: block;
margin: 0 0 0 8px;
}
.cselect.is-open .cselect__rendered:after {
content: "▴";
}
.cselect__rendered-text {
display: inline-block;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
.cselect__options {
position: absolute;
top: 100%;
left: 0;
margin: 6px 0;
padding: 6px;
width: 100%;
max-height: 264px;
color: #fff;
background: #333;
border-radius: 6px;
transform: translateY(-6px) scale(0.98);
transform-origin: center top;
opacity: 0;
visibility: hidden;
transition-property: transform, visibility, opacity;
transition-duration: 0.3s;
transition-timing-function: ease;
overflow: auto;
z-index: 999;
}
.cselect__options::-webkit-scrollbar {
width: 4px;
}
.cselect__options::-webkit-scrollbar-track {
background: transparent;
border-radius: 4px;
}
.cselect__options::-webkit-scrollbar-thumb {
background: #555;
border-radius: 4px;
}
.cselect__options::-webkit-scrollbar-thumb:hover {
background: #777;
}
.cselect__options.is-on-top {
top: auto;
bottom: 100%;
}
.cselect.is-open .cselect__options {
transform: translateY(0) scale(1);
opacity: 1;
visibility: visible;
transition-property: transform, opacity;
}
.cselect__search {
display: block;
margin: 0 0 6px;
padding: 2px 6px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #333;
line-height: 1.5;
background: #fff;
border: 0;
border-radius: 6px;
}
.cselect__option {
display: block;
padding: 6px;
width: 100%;
font: inherit;
font-size: 16px;
font-weight: normal;
color: #fff;
line-height: 1.5;
text-align: left;
text-decoration: none;
background: transparent;
border: 0;
border-radius: 6px;
cursor: pointer;
}
.cselect__option:hover {
background: #555;
}
.cselect__option.is-selected {
color: #999;
background: transparent;
cursor: default;
}
.cselect__option.is-hidden {
display: none;
}
<form action="./" method="get">
<button type="submit">GET</button>
<br><br><br><br>
<div style="max-width: 256px;">
<select name="select1" id="select1" class="js-select" required>
<option value="" selected disabled>Please select</option>
<option value="bunny">Perú</option>
<option value="kitten">Luís</option>
<option value="hamster">Hamster</option>
</select>
</div>
</form>
You need to update your function handleSearchFn to convert characters to the base ones (you can just replace yours with next one):
#handleSearchFn() {
const searchPhrase = this.#searchEl.value.normalize('NFD').replace(/[\u0300-\u036f]/g, "").toLowerCase();
for (const optionEl of this.#optionEls) {
if (optionEl.textContent.toLowerCase().indexOf(searchPhrase) > -1) {
optionEl.classList.remove(this.options.classNames.hidden);
} else {
optionEl.classList.add(this.options.classNames.hidden);
}
}
}

Iterating through list of <div> on keydown event by pressing down and up key

This function iterate through list of <div> on keydown event by pressing down and up key. Also when press enter key, <div> text content is inserted into the <input> field. I would like to know if the logic of the function I made is correct or things can be done differently. Also I would like to know if the function can be shortened.
const inputField = document.querySelector("input");
inputField.addEventListener("keydown", selectElement);
function selectElement(e) {
const elements = document.querySelectorAll(".element");
const len = elements.length - 1;
if (e.keyCode == '40') {
for (let i = 0; i <= len; i++) {
if (elements[i].classList.contains("hover") && i != len) {
elements[i].classList.remove("hover");
} else if (elements[i].dataset.read !== "true") {
elements[i].classList.add("hover");
elements[i].dataset.read = true;
break;
}
}
}
if (e.keyCode == '38') {
for (let i = len; i >= 0; i--) {
if (elements[i].classList.contains("hover") && i != 0) {
elements[i].classList.remove("hover");
elements[i].dataset.read = false;
} else if (elements[i].dataset.read !== "false") {
elements[i].classList.add("hover");
break;
}
}
}
if (e.keyCode == '13') {
for (let i = 0; i <= len; i++) {
if (elements[i].classList.contains("hover")) {
inputField.value = elements[i].textContent;
}
}
}
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
.container {
display: inline-block;
padding: 10px;
background-color: #eee;
border: 1px solid #ddd;
}
input {
width: 200px;
margin: 5px;
padding: 5px;
border: 1px solid #ddd;
}
.element {
width: 200px;
margin: 5px;
padding: 10px;
text-align: center;
background-color: #fff;
border: 1px solid #ddd;
}
.hover {
background-color: #c1dff3;
}
<div class="container">
<input type="text">
<div class="element">Uno</div>
<div class="element">Dos</div>
<div class="element">Tres</div>
<div class="element">Cuatro</div>
<div class="element">Cinco</div>
</div>
I've created some small functions and changed the logic a little bit. You can check this solution to get some insight.
const inputField = document.querySelector("#input");
const elements = document.querySelectorAll(".element");
inputField.focus();
let [activeIndex, length] = [-1, elements.length];
inputField.addEventListener("keydown", handleKeyDown);
function handleKeyDown(e) {
const {key} = e;
if(key === "ArrowDown") moveDown(e);
if(key === "ArrowUp") moveUp(e);
if(key === 'Enter') insertText(e);
}
function moveDown(e) {
activeIndex = (activeIndex+1) % length;
navigateThroughElement(activeIndex);
}
function moveUp(e) {
if(activeIndex === -1) activeIndex = 0;
activeIndex = (length + (activeIndex-1)) % length;
navigateThroughElement(activeIndex);
}
function navigateThroughElement(index) {
elements.forEach(el => {
if(el.classList.contains('hover')) el.classList.remove('hover');
})
elements[index].classList.add('hover');
}
function insertText(e) {
if(activeIndex >= 0) {
e.target.value = elements[activeIndex].innerText;
}
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
}
.container {
display: inline-block;
padding: 10px;
background-color: #eee;
border: 1px solid #ddd;
}
input {
width: 200px;
margin: 5px;
padding: 5px;
border: 1px solid #ddd;
}
.element {
width: 200px;
margin: 5px;
padding: 10px;
text-align: center;
background-color: #fff;
border: 1px solid #ddd;
}
.hover {
background-color: #c1dff3;
}
<div class="container">
<input type="text" id="input" placeholder="press up/down..." autocomplete="off">
<div class="element">Uno</div>
<div class="element">Dos</div>
<div class="element">Tres</div>
<div class="element">Cuatro</div>
<div class="element">Cinco</div>
</div>
I have arranged the code as follows.
html:
<div>
<div>
<input type="text">
</div>
<div class="container">
<div class="element">Uno</div>
<div class="element">Dos</div>
<div class="element">Tres</div>
<div class="element">Cuatro</div>
<div class="element">Cinco</div>
</div>
</div>
Javascript:
const inputField = document.querySelector("input");
inputField.addEventListener("keydown", selectElement);
window.curIndex = -1;
function selectElement(e) {
var totalItems = document.querySelectorAll('.element').length;
var selected = document.querySelector('.element.hover');
// console.log(e.keyCode);
if(e.keyCode === 13 && selected){
inputField.value = selected.textContent;
}
else {
console.log(window.curIndex);
if(e.keyCode === 40){
window.curIndex = (window.curIndex + 1) % totalItems;
}
else if(e.keyCode === 38){
window.curIndex = window.curIndex <= 0? totalItems - 1: window.curIndex - 1;
}
console.log(window.curIndex);
if(selected){
selected.classList.remove('hover');
}
var selector = '.container .element:nth-child(' + (window.curIndex + 1) + ')';
console.log(selector);
selected = document.querySelector(selector);
if(selected){
console.log('Adding hover');
selected.classList.add('hover');
}
}
}
Check the fiddle

:before with position: absolute causing line breaks and moving elements

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>

I'm having trouble with the .innerHTML not working properly

I cant get the "if" part to work, thus not outputting the text Test0 or Test1 to my "Test" div after I press either of the links. I have checked for any error messages, which there are none of. I also added the changing color to check if it was the event listeners that were the problem, but that worked fine. So my only problem is that I can't get the text to output.
var testEl = document.querySelector("#test");
var menyEl = document.querySelectorAll("a");
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].addEventListener("click", byttInfo);
}
function byttInfo(e) {
if (e.target.className === "ikke_aktiv") {
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].className = "ikke_aktiv";
}
e.target.className = "aktiv"
}
if (e.target.value === 0) {
testEl.innerHTML = "Test0"
} else if (e.target.value === 1) {
testEl.innerHTML = "Test1"
}
}
.innpakning {
width: 80%;
background-color: #708790;
padding: 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.loddrett_meny {
display: flex, block;
flex: 1;
padding-top: 6px;
}
.loddrett_meny a {
display: block;
text-align: center;
padding: 12px;
margin: 4px;
color: white;
font-size: 20px;
text-decoration: none;
}
.loddrett_meny a:hover {
background-color: black;
border-radius: 8px;
transition: 0.75s;
}
.aktiv {
background-color: #3cb500;
}
.ikke_aktiv {
background-color: #555;
}
.innhold,
#test {
flex: 6;
height: auto;
background-color: #a6d4a3;
padding-left: 2px;
border-radius: 2px;
margin: 10px;
margin-right: 0px;
font-size: 20px;
}
<div class="innpakning">
<div class="loddrett_meny">
<a class="aktiv" value="0">Kapittel 20</a>
<a class="ikke_aktiv" value="1">Kapittel 20.1</a>
</div>
<div id="test"></div>
</div>
You need to use quotes around values or convert them to Number
if(e.target.value === "0"){
testEl.innerHTML = "Test0"
}else if(Number(e.target.value) === 1){
testEl.innerHTML = "Test1"
}
Adding on what Abdulah Proho said: You can't add the value attribute to a tags, so it's discarded and e.target.value. You may use data attributes for this:
var testEl = document.querySelector("#test");
var menyEl = document.querySelectorAll("a");
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].addEventListener("click", byttInfo);
}
function byttInfo(e) {
if (e.target.className === "ikke_aktiv") {
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].className = "ikke_aktiv";
}
e.target.className = "aktiv"
}
if (e.target.dataset.value === "0") {
testEl.innerHTML = "Test0"
} else if (e.target.dataset.value === "1") {
testEl.innerHTML = "Test1"
}
}
.innpakning {
width: 80%;
background-color: #708790;
padding: 10px;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.loddrett_meny {
display: flex, block;
flex: 1;
padding-top: 6px;
}
.loddrett_meny a {
display: block;
text-align: center;
padding: 12px;
margin: 4px;
color: white;
font-size: 20px;
text-decoration: none;
}
.loddrett_meny a:hover {
background-color: black;
border-radius: 8px;
transition: 0.75s;
}
.aktiv {
background-color: #3cb500;
}
.ikke_aktiv {
background-color: #555;
}
.innhold,
#test {
flex: 6;
height: auto;
background-color: #a6d4a3;
padding-left: 2px;
border-radius: 2px;
margin: 10px;
margin-right: 0px;
font-size: 20px;
}
<div class="innpakning">
<div class="loddrett_meny">
<a class="aktiv" data-value="0">Kapittel 20</a>
<a class="ikke_aktiv" data-value="1">Kapittel 20.1</a>
</div>
<div id="test"></div>
</div>
e.target.value is undefined. Due to anchor tags usually not having value attribute. The 'a' elements don't have the data prototype for value.
You should try getting the value using:
e.target.getAttribute('value')
Also while comparing try using:
e.target.getAttribute('value') === '0'
Given the value you get from getAttribute function is a string the value getting compared with should also be a string.
The following works I have tested it as a snippet. Trying copying this entirely and replace it with your js. you need to use the getAttribute method to get the value.
var testEl = document.getElementById("test");;
var menyEl = document.querySelectorAll("a");
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].addEventListener("click", byttInfo);
}
function byttInfo(e) {
if (e.target.className === "ikke_aktiv") {
for (var i = 0; i < menyEl.length; i++) {
menyEl[i].className = "ikke_aktiv";
}
e.target.className = "aktiv"
}
if (e.target.getAttribute("value") === "0"){
testEl.innerText = "Test0"
} else if (e.target.getAttribute("value") === "1") {
testEl.innerText = "Test1"
}
}

Compare input value with li content in pure Javascript

I have to do some kind of ToDo list, where I have input and button to Add item to ul list. And now I done everything except compare every li item with input value. My question is how to compare every li content with value input to prevent duplicate items. Here is the code https://jsfiddle.net/qoLtxfaw/1/
// Variables
var ul = document.getElementById("taskList");
var task = document.getElementById("task");
var btn = document.querySelector('button');
var listItem = document.getElementsByTagName("LI");
// Append close btn to each list item
for (var i = 0; i < listItem.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "js-close";
span.appendChild(txt);
listItem[i].appendChild(span);
}
// Click on a close button to hide the current list item
var close = document.getElementsByClassName("js-close");
for (var i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.className = 'js-hide';
}
}
// proveravati ima li ul odnosno liste, ukoliko ne proverimo a nema ceo kod ce prestati da se izvrsava
if (ul) {
ul.onmouseover = function(event) {
var target = event.target;
target.style.background = '#efebeb';
};
ul.onmouseout = function(event) {
var target = event.target;
target.style.background = '';
};
}
// Add item to list on btn
btn.addEventListener('click', addItem);
// Add item to list on enter
task.onkeyup = function(e) {
if (e.keyCode == 13) {
addItem();
}
};
// // Add item to list
function addItem() {
var li = document.createElement("li");
var inputValue = document.getElementById('task').value;
li.setAttribute('id', task.value);
li.appendChild(document.createTextNode(task.value));
// ul.appendChild(li);
// compare every li item with inputValue
if (inputValue) { //if input value is true and has some value
//go trough all li items
for (var i = 0; i < listItem.length; i++) {
// compare every li item with inputValue
}
// Duplicate values don't allow in list
if (!inputValue) {
alert("No empty values are allowed!");
li.className = 'js-btn-disable';
} else {
ul.appendChild(li);
}
document.getElementById("task").value = "";
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "js-close";
span.appendChild(txt);
li.appendChild(span);
for (var i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.className = 'js-hide';
}
}
}
}
#wrapper {
width: 500px;
margin: 0 auto;
background: #00bcd4;
border: 1px solid #f1f0f0;
padding-left: 10px;
font-size: 1.2em;
}
#wrapper #task {
background: transparent;
color: #ffffff;
font-size: 1.2em;
width: 80%;
height: 35px;
border: none;
border-bottom: 2px solid #ffffff;
outline: none;
margin: 15px 0 5px 0;
}
#wrapper #task ::-webkit-input-placeholder {
/* Chrome/Opera/Safari */
color: #ffffff;
}
#wrapper #task ::-moz-placeholder {
/* Firefox 19+ */
color: #ffffff;
}
#wrapper #task :-ms-input-placeholder {
/* IE 10+ */
color: #ffffff;
}
#wrapper #task :-moz-placeholder {
/* Firefox 18- */
color: #ffffff;
}
#wrapper button {
font-size: 1.2em;
border: 2px solid #ffffff;
background: transparent;
color: #ffffff;
padding: 5px 10px;
outline: none;
cursor: pointer;
}
#wrapper ul#taskList {
padding: 0;
background: #ffffff;
list-style-type: none;
text-align: left;
margin-bottom: 0;
margin-left: -10px;
}
#wrapper ul#taskList li {
padding: 10px;
position: relative;
cursor: pointer;
}
/* Style the close button */
.js-close {
position: absolute;
right: 0;
top: 0;
padding: 10px;
}
.js-close:hover {
color: #ffffff;
}
.js-hide {
display: none;
}
.js-background {
background: #efebeb;
}
.js-btn-disable {
opacity: 0.65;
cursor: not-allowed;
}
/*# sourceMappingURL=style.css.map */
<div id="wrapper">
<input type="text" id="task" />
<button>Add</button>
<ul id="taskList"></ul>
</div>
Have a look at this. I use firstChild and I moved the validation to the top of the function.
I use the inputValue after validating it but task everywhere else.
DRY - Don't Repeat Yourself
// Variables
var ul = document.getElementById("taskList");
var task = document.getElementById("task");
var btn = document.querySelector('button');
var listItem = document.getElementsByTagName("LI");
task.focus();
// Append close btn to each list item
for (var i = 0; i < listItem.length; i++) {
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "js-close";
span.appendChild(txt);
listItem[i].appendChild(span);
}
// Click on a close button to hide the current list item
var close = document.getElementsByClassName("js-close");
for (var i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.className = 'js-hide';
}
}
// proveravati ima li ul odnosno liste, ukoliko ne proverimo a nema ceo kod ce prestati da se izvrsava
if (ul) {
ul.onmouseover = function(event) {
var target = event.target;
target.style.background = '#efebeb';
};
ul.onmouseout = function(event) {
var target = event.target;
target.style.background = '';
};
}
// Add item to list on btn
btn.addEventListener('click', addItem);
// Add item to list on enter
task.onkeyup = function(e) {
if (e.keyCode == 13) {
addItem();
}
};
// // Add item to list
function addItem() {
var inputValue = task.value.trim();
task.value = "";
task.focus();
// Empty or Duplicate values don't allow in list
if (!inputValue) {
alert("No empty values are allowed!");
return
}
var listItem = document.querySelectorAll("#taskList li");
for (var i = 0; i < listItem.length; i++) {
if (inputValue == listItem[i].firstChild.textContent) {
alert("No duplicate values are allowed!");
return
}
}
var li = document.createElement("li");
li.setAttribute('id', inputValue);
li.appendChild(document.createTextNode(inputValue));
ul.appendChild(li);
var span = document.createElement("SPAN");
var txt = document.createTextNode("\u00D7");
span.className = "js-close";
span.appendChild(txt);
li.appendChild(span);
for (var i = 0; i < close.length; i++) {
close[i].onclick = function() {
var div = this.parentElement;
div.className = 'js-hide';
}
}
}
#wrapper {
width: 500px;
margin: 0 auto;
background: #00bcd4;
border: 1px solid #f1f0f0;
padding-left: 10px;
font-size: 1.2em;
}
#wrapper #task {
background: transparent;
color: #ffffff;
font-size: 1.2em;
width: 80%;
height: 35px;
border: none;
border-bottom: 2px solid #ffffff;
outline: none;
margin: 15px 0 5px 0;
}
#wrapper #task ::-webkit-input-placeholder {
/* Chrome/Opera/Safari */
color: #ffffff;
}
#wrapper #task ::-moz-placeholder {
/* Firefox 19+ */
color: #ffffff;
}
#wrapper #task :-ms-input-placeholder {
/* IE 10+ */
color: #ffffff;
}
#wrapper #task :-moz-placeholder {
/* Firefox 18- */
color: #ffffff;
}
#wrapper button {
font-size: 1.2em;
border: 2px solid #ffffff;
background: transparent;
color: #ffffff;
padding: 5px 10px;
outline: none;
cursor: pointer;
}
#wrapper ul#taskList {
padding: 0;
background: #ffffff;
list-style-type: none;
text-align: left;
margin-bottom: 0;
margin-left: -10px;
}
#wrapper ul#taskList li {
padding: 10px;
position: relative;
cursor: pointer;
}
/* Style the close button */
.js-close {
position: absolute;
right: 0;
top: 0;
padding: 10px;
}
.js-close:hover {
color: #ffffff;
}
.js-hide {
display: none;
}
.js-background {
background: #efebeb;
}
.js-btn-disable {
opacity: 0.65;
cursor: not-allowed;
}
/*# sourceMappingURL=style.css.map */
<div id="wrapper">
<input type="text" id="task" />
<button>Add</button>
<ul id="taskList"></ul>
</div>

Categories