Created a validation form in vanilla JS. Due to my little experience, I have a bunch of "if" here, I can not transfer the coordinates of Yandex.Maps to another function for validation, and it is still unknown what. With the coordinates, I "crutch" by saving them to an invisible div :-) I deleted my Api key, so I won't be able to get the coordinates in the example, I don't know if it's worth giving the key to public access :-) The code is working.
Tell me how you can reduce the code or how to do button validation without my crutch with a div :-)
let errorName = document.querySelector(".name-error");
let errorNumber = document.querySelector(".number-error");
let errorEmail = document.querySelector(".email-error");
let errorMap = document.querySelector(".map-error");
let completed = document.querySelector(".mailForm__completed");
function viodChecker() {
let name = document.querySelector(".mailForm__name").value;
let number = document.querySelector(".mailForm__number").value;
let Email = document.querySelector(".mailForm__email").value;
let Coords = document.querySelector(".coords");
if (name === "") {
errorName.style.display = "block";
}
if (name !== "") {
errorName.style.display = "none";
}
if (number === "") {
errorNumber.style.display = "block";
}
if (number !== "") {
errorNumber.style.display = "none";
}
if (Email.indexOf("#") === -1) {
errorEmail.style.display = "block";
}
if (Email.indexOf("#") !== -1) {
errorEmail.style.display = "none";
}
if (Email.indexOf("#") !== -1) {
errorEmail.style.display = "none";
}
if (Coords.textContent === "") {
errorMap.style.display = "block";
}
if (Coords.textContent !== "") {
errorMap.style.display = "none";
}
if (name !== "" && number !== "" && Email.indexOf("#") !== -1 && Coords.textContent !== "") {
completed.style.display = "block";
}
}
ymaps.ready(init);
function init() {
var myPlacemark,
myMap = new ymaps.Map('map', {
center: [40.391917, -73.524590],
zoom: 17,
controls: ['geolocationControl', 'searchControl']
});
// We listen to the click on the map.
myMap.events.add('click', function (e) {
var coords = e.get('coords');
// If the label has already been created, just move it.
if (myPlacemark) {
myPlacemark.geometry.setCoordinates(coords);
}
// If not, we create.
else {
myPlacemark = createPlacemark(coords);
myMap.geoObjects.add(myPlacemark);
// Listen to the drag-and-drop event on the label.
myPlacemark.events.add('dragend', function () {
getAddress(myPlacemark.geometry.getCoordinates());
});
}
getAddress(coords);
});
// Create a label.
function createPlacemark(coords) {
return new ymaps.Placemark(coords, {
iconCaption: 'search...'
}, {
preset: 'islands#violetDotIconWithCaption',
draggable: true
});
}
// Coords
function getAddress(coords) {
myPlacemark.properties.set('iconCaption', 'поиск...');
ymaps.geocode(coords).then(function (res) {
var firstGeoObject = res.geoObjects.get(0);
myPlacemark.properties
.set({
// We form a line with data about the object.
iconCaption: [
firstGeoObject.geometry.getCoordinates(),
].filter(Boolean).join(', '),
// We set the line with the object address as the balloon content.
balloonContent: firstGeoObject
});
document.querySelector(".coords").textContent = firstGeoObject.geometry.getCoordinates();
});
}
}
body, .mailForm {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
input, textarea {
font-size: 14px;
margin: 10px 0 0 0;
width: 350px;
resize: none;
}
.mailForm__map {
margin-top: 10px;
}
button {
margin: 10px 0 0 0;
font-size: 14px;
cursor: pointer;
}
.mailForm__comment {
display: inline-block;
height: 200px;
}
.mailForm__error, .mailForm__completed, .coords {
display: none;
color: red;
font-size: 16px;
font-weight: bold;
margin-top: 10px;
}
.mailForm__completed {
color: green;
font-size: 20px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="style.css" rel="stylesheet">
<title>Mail Form</title>
<script src="https://api-maps.yandex.ru/2.1/?apikey=5200498b-0123-4796-1-bee4ea5473212&lang=ru_RU" type="text/javascript">
</script>
</head>
<body>
<div class="mailForm">
<input class="mailForm__name" placeholder="Name" type="text" value="">
<input class="mailForm__number" placeholder="Tel" type="tel" value="">
<input class="mailForm__email" placeholder="Email" type="email" value="">
<div id="map" class="mailForm__map" style="width: 100%; height: 200px">
</div>
<textarea class="mailForm__comment" maxlength="500" placeholder="Comment" type="text" value=""></textarea>
<button onclick="viodChecker()">SEND</button>
<div class="mailForm__error name-error">NAME ERROR</div>
<div class="mailForm__error number-error">TEL ERROR</div>
<div class="mailForm__error email-error"># ERROR</div>
<div class="mailForm__error map-error">COORDS ERROR</div>
<div class="mailForm__completed">NICE!</div>
<div class="coords"></div>
</div>
<script src="script.js"></script>
</body>
</html>
Related
I have example of drag and drop: https://codepen.io/ilq-trifonow/pen/GRmaoBP
Now I can move elements by any part of them. But I want to use a <p> tag with a class="drag-handler" for this. How can I do it?
index.html
<html>
<head>
<meta charset="utf-8">
<title>Список задач с drag & drop</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="tasks">
<h1 class="tasks__title">To do list</h1>
<div class="tasks__list">
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn HTML</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn CSS</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn JavaScript</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>learn PHP</p></div>
<div class="tasks__item"><p class="drag-handler">Drag it</p><p>stay alive</p></div>
</div>
</div>
</body>
</html>
index.js
const tasksListElement = document.querySelector(`.tasks__list`);
const taskElements = tasksListElement.querySelectorAll(`.tasks__item`);
for (const task of taskElements) {
task.draggable = true;
}
tasksListElement.addEventListener(`dragstart`, (evt) => {
evt.target.classList.add(`selected`);
});
tasksListElement.addEventListener(`dragend`, (evt) => {
evt.target.classList.remove(`selected`);
});
const getNextElement = (cursorPosition, currentElement) => {
const currentElementCoord = currentElement.getBoundingClientRect();
const currentElementCenter = currentElementCoord.x + currentElementCoord.width / 2;
const nextElement = (cursorPosition < currentElementCenter) ?
currentElement :
currentElement.nextElementSibling;
return nextElement;
};
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const activeElement = tasksListElement.querySelector(`.selected`);
const currentElement = evt.target;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
if (
nextElement &&
activeElement === nextElement.previousElementSibling ||
activeElement === nextElement
) {
return;
}
tasksListElement.insertBefore(activeElement, nextElement);
});
You can set draggable=true for all inner tag (say p.drag-handler) and then in dragover listener use parentElement as a currentElement that must be dragged.
Here is working sample:
const tasksListElement = document.querySelector(`.tasks__list`);
const taskElements = tasksListElement.querySelectorAll(`.drag-handler`);
for (const task of taskElements) {
task.draggable = true;
}
tasksListElement.addEventListener(`dragstart`, (evt) => {
evt.target.parentElement.classList.add(`selected`);
});
tasksListElement.addEventListener(`dragend`, (evt) => {
evt.target.parentElement.classList.remove(`selected`);
});
const getNextElement = (cursorPosition, currentElement) => {
const currentElementCoord = currentElement.getBoundingClientRect();
const currentElementCenter = currentElementCoord.x + currentElementCoord.width / 2;
const nextElement = (cursorPosition < currentElementCenter) ?
currentElement :
currentElement.nextElementSibling;
return nextElement;
};
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const activeElement = tasksListElement.querySelector(`.selected`);
const currentElement = evt.target.parentElement;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
tasksListElement.insertBefore(activeElement, nextElement);
});
html, body {
margin: 0;
padding: 0;
}
body {
font-family: "Tahoma", sans-serif;
font-size: 18px;
line-height: 25px;
color: #164a44;
background-color: #b2d9d0;
}
.tasks__title {
margin: 50px 0 20px 0;
text-align: center;
text-transform: uppercase;
}
.tasks__list {
display: flex;
justify-content: center;
}
.tasks__item {
width: 250px;
margin-right: 10px;
padding: 5px;
text-align: center;
border: 2px dashed #b2d9d0;
border-radius: 10px;
background-color: #dff2ef;
transition: background-color 0.5s;
}
.tasks__item:last-child {
margin-bottom: 0;
}
.selected {
opacity: 0.6;
}
.drag-handler {
background-color: #ddd;
cursor: move;
}
<div class="tasks">
<h1 class="tasks__title">To do list</h1>
<div class="tasks__list">
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn HTML</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn CSS</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn JavaScript</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>learn PHP</p></div>
<div class="tasks__item" ><p class="drag-handler">Drag it</p><p>stay alive</p></div>
</div>
</div>
If I understand you correctly, you want to move the element by selecting inside of the paragraph element to move the entire containing div element.
In the scope of what you have written, I would simply use an implicit reference inside of the child node to set the draggable element when you select it
for (const task of taskElements) {
task.firstChild.addEventListener(`mousedown`, (evt) => {
task.draggable = true
task.classList.add(`selected`)
})
}
Further to this, I wouldn't rely on DOM reads / writes as it tends to be more expensive than simply holding onto a reference to the element you are looking for:
let activeElement
for (const task of taskElements) {
task.firstChild.addEventListener(`mousedown`, (evt) => {
task.draggable = true
activeElement = task
})
}
...
tasksListElement.addEventListener(`dragover`, (evt) => {
evt.preventDefault();
const currentElement = evt.target;
const isMoveable = activeElement !== currentElement &&
currentElement.classList.contains(`tasks__item`);
if (!isMoveable) {
return;
}
const nextElement = getNextElement(evt.clientX, currentElement);
if (
nextElement &&
activeElement === nextElement.previousElementSibling ||
activeElement === nextElement
) {
return;
}
tasksListElement.insertBefore(activeElement, nextElement);
});
I gave this a try in your codepen, should be fine.
I'm studying a project by Wes Bos where you were tasked with adding shift click functionality to a series of list items.
I completed this and wanted to go further by then adding the ability to deselect these list items which I did (see commented javascript code).
Then I wanted to take that solution and apply DRY principles and that's where things became difficult.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hold Shift to Check Multiple Checkboxes</title>
</head>
<body>
<style>
html {
font-family: sans-serif;
background: #ffc600;
}
.inbox {
max-width: 400px;
margin: 50px auto;
background: white;
border-radius: 5px;
box-shadow: 10px 10px 0 rgba(0,0,0,0.1);
}
.item {
display: flex;
align-items: center;
border-bottom: 1px solid #F1F1F1;
}
.item:last-child {
border-bottom: 0;
}
input:checked + p {
background: #F9F9F9;
text-decoration: line-through;
}
input[type="checkbox"] {
margin: 20px;
}
input:hover {
cursor: pointer;
}
p {
margin: 0;
padding: 20px;
transition: background 0.2s;
flex: 1;
font-family:'helvetica neue';
font-size: 20px;
font-weight: 200;
border-left: 1px solid #D1E2FF;
}
</style>
<!--
The following is a common layout you would see in an email client.
When a user clicks a checkbox, holds Shift, and then clicks another checkbox a few rows down, all the checkboxes inbetween those two checkboxes should be checked.
-->
<div class="inbox">
<div class="item">
<input type="checkbox">
<p>This is an inbox layout.</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check one item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Hold down your Shift key</p>
</div>
<div class="item">
<input type="checkbox">
<p>Check a lower item</p>
</div>
<div class="item">
<input type="checkbox">
<p>Everything in between should also be set to checked</p>
</div>
<div class="item">
<input type="checkbox">
<p>Try to do it without any libraries</p>
</div>
<div class="item">
<input type="checkbox">
<p>Just regular JavaScript</p>
</div>
<div class="item">
<input type="checkbox">
<p>Good Luck!</p>
</div>
<div class="item">
<input type="checkbox">
<p>Don't forget to tweet your result!</p>
</div>
</div>
<script>
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
input.forEach(box => box.addEventListener('click', checkFunction));
// const input = document.querySelectorAll('.item input[type="checkbox"]');
// let lastchecked;
//
// function checkFunction (event) {
// let inbetween = false;
// if (event.shiftKey && this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = true;
// }
// });
// } else if (event.shiftKey && !this.checked) {
// input.forEach(box => {
// if (box === this || box === lastChecked) {
// inbetween = !inbetween;
// }
// if (inbetween) {
// box.checked = false;
// }
// });
// }
// lastChecked = this;
// }
//
// input.forEach(box => box.addEventListener('click', checkFunction));
</script>
</body>
</html>
I expected at that point that all I had to do was call the function where the code was repeated, but this time using a boolean parameter, however it then says that the inbetween variable is undefined. I'm confused at this point. If I define it within the new function it just ticks everything and doesn't change the variable back to false etc.
I hope that makes sense. I'd love to know where I'm going wrong for the future.
Thanks all.
I'd suggest declaring tickBox() inside of checkFunction(). This is feasible because it's never called elsewhere. Then, it can have access to the scope of checkFunction() including the inbetween variable:
const input = document.querySelectorAll('.item input[type="checkbox"]');
let lastchecked;
function checkFunction (event) {
let inbetween = false;
function tickBox(boolean) {
input.forEach(box => {
if (box === this || box === lastChecked) {
inbetween = !inbetween;
}
if (inbetween) {
box.checked = boolean;
}
});
}
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
lastChecked = this;
}
input.forEach(box => box.addEventListener('click', checkFunction));
FYI, you can simplify your if/else from this:
if (event.shiftKey && this.checked) {
tickBox(true);
} else if (event.shiftKey && !this.checked) {
tickBox(false);
}
to this:
if (event.shiftKey) {
tickBox(this.checked);
}
Which then means that you don't actually need a separate function for tickBox() any more.
I am learning javascript and practicing by making a simple book list app.
I wanted to add the books to local storage. But the array I want to push the values into is starting as undefined and then it is set to null. And the if else statement is not working it runs through both the if statement despite the fact that the condition should return true. It starts on line 32 First a variable booklist is declared then it checks to see if bookLists exists in local storage if it does not it sets the value bookLists to a empty array ELSE it grabs booklist from local storage and parses the array adds the book to the book list. Then sets the item to local storage. At least that is what I was trying to do. Any ideas what I am not doing correctly? 0.0
The HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
<link rel="stylesheet" href="css/main.css">
<style>
.correct {
color: antiquewhite;
padding: 7px;
margin: 5px 0px 16px 0px;
border: 3px forestgreen solid;
border-radius: 6px;
background-color: forestgreen;
}
.error {
color: antiquewhite;
padding: 7px;
margin: 5px 0px 16px 0px;
border: 3px firebrick solid;
border-radius: 6px;
background-color: firebrick;
}
</style>
<title>Book List Generator</title>
</head>
<body>
<div id="container" class="container booklistContainer">
<h1 class="booklistH1">Add a Book to the Booklist &:<)</h1>
<form id="form">
<div>
<label for="title">Title</label>
<input type="text" id="title" class="u-full-width">
</div>
<div>
<label for="author">Author</label>
<input type="text" id="author" class="u-full-width">
</div>
<div>
<label for="isbn">ISBN#</label>
<input type="text" id="isbn" class="u-full-width">
</div>
<div>
<button class="u-full-width" id="submit">Add a bookmark</button>
</div>
<hr>
<table class="u-full-width">
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
<th></th>
</tr>
</thead>
<tbody id="BookListBody"></tbody>
</table>
</div>
<script src="Js/booklistGenerator.js"></script>
</body>
</html>
The Css
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 1.5rem;
}
.booklistContainer {
margin-bottom: 7rem;
}
#media screen and (max-width: 519px) {
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 1rem;
}
}
#media screen and (max-width: 352px) {
.booklistH1 {
font-size: 0.9rem;
}
}
#media screen and (max-width: 352px) {
.booklistH1 {
letter-spacing: 1px;
margin: 3rem 0rem;
font-size: 0.8rem;
}
}
The Javascript
// adding a event listener
const sub = document.getElementById("submit").addEventListener("click", valuerRetrivel);
const removeBook = document.getElementById("BookListBody").addEventListener("click", bookRemover);
// the book constructer
function BookConstructer (title, author, isbn){
this.title = title;
this.author = author;
this.isbn = isbn;
};
// The Ui constructer
function UiConstructor() {}
// adding a method to the Ui constructer prtotype
UiConstructor.prototype.addBookToList = function(book){
// grab the table body
const list = document.getElementById("BookListBody");
//create the table row to append the table cells
const row = document.createElement("tr");
// add the cells to the table row using templet strings
row.innerHTML = `
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
<td>X</td>
`;
// append to the table body
list.appendChild(row);
let bookList;
if (localStorage.getItem("bookList" === null)) {
bookList = [];
}else {
bookList = JSON.parse(localStorage.getItem("bookList"));
}
bookList.push(book);
localStorage.setItem("bookList", JSON.stringify(bookList));
alert("task saved");
}
UiConstructor.prototype.alertMessage = function(message, className) {
// create and append the alert message
const alertDiv = document.createElement("div");
alertDiv.className = `alert ${className}`;
alertDiv.setAttribute("id", "alert");
const alertDivTextNode = document.createTextNode(message);
alertDiv.appendChild(alertDivTextNode);
const parent = document.getElementById("container");
const form = document.getElementById("form");
parent.insertBefore(alertDiv, form);
// remove the alert after 3 seconds
setTimeout(function(){
document.getElementById("alert").remove();
},3000);
}
UiConstructor.prototype.successMessage = function(message, className) {
// create and append the success message
const successDiv = document.createElement("div");
successDiv.className = `success ${className}`;
successDiv.setAttribute("id", "success");
const successtDivTextNode = document.createTextNode(message);
successDiv.appendChild(successtDivTextNode);
const parent = document.getElementById("container");
const form = document.getElementById("form");
parent.insertBefore(successDiv, form);
console.log(UiConstructor);
// remove the alert after 3 seconds
setTimeout(function(){
document.getElementById("success").remove();
},3000);
}
// retriving the form values
function valuerRetrivel(e) {
// initating a Ui constructor to accses its methods
const ui = new UiConstructor();
// reguler expression that checks for whitespace
const regexp = /^\s+$/;
// retriving the form input values
const title = document.getElementById("title").value,
author = document.getElementById("author").value,
isbn = document.getElementById("isbn").value;
const resultTitle = regexp.test(title);
const resultAuthor = regexp.test(author)
const resultIsbn = regexp.test(isbn);
// cheacking for white space
if (resultTitle === true
|| resultAuthor === true
|| resultIsbn === true
|| title === ""
|| author === ""
|| isbn === "") {
// calling the alert message and passing the arguments
ui.alertMessage("All form fields must have content", "error");
e.preventDefault();
return false;
}else {
// calling the book constructer function to create a book object
const book = new BookConstructer(title, author, isbn);
// initating the ui constructer and creating a new book object
ui.addBookToList(book);
console.log(ui);
// calling the success message method and passing the arguments
ui.successMessage("Success!", "correct");
// clearing the current input values
const titleClear = document.getElementById("title").value = "",
authorClear = document.getElementById("author").value = "",
isbnClear = document.getElementById("isbn").value = "";
e.preventDefault();
return true;
}
};
function bookRemover(e) {
if (e.target.className === "delete") {
if(confirm("Are you sure you want to delete this link?")) {
e.target.parentElement.parentElement.remove();
e.preventDefault();
}
}
}
You have a typo
if (localStorage.getItem("bookList" === null)) {
which is always false.
This causes the bookList to never be instantiated from the true clause, and also as a result the storage item is attempted to be used, which is where the null parse comes in from
JSON.parse(localStorage.getItem("bookList"))
I am having trouble to look where the problem is.
I was able to create the game board and I will be implementing AI soon.
For now, I need to make it work so that everytime the game is over, it will restart a new one and resume.
However when it reaches to that point, it stops working.
From my what I've read, I may not be binding the events correctly, but I am still left puzzled.
Can anyone pinpoint what is wrong with my code?
$(document).ready(function(){
var human;
var computer;
var board = new Board()
var game;
function Human(symbol){
this.name = "Player",
this.symbol = symbol;
}
function Computer(symbol){
this.name = "Computer",
this.symbol = symbol;
}
//Modal opens when page is rendered. User can choose symbol
$("#myModal").modal()
$("#xPlayer").on('click',function(){
human = new Human("X");
computer = new Computer("O");
board.initalize();
game = new Game(human)
game.play();
})
$("#oPlayer").on('click',function(){
human = new Human("O")
computer = new Computer("X");
board.initalize();
game = new Game(computer)
game.play();
})
//Board constuctor
function Board(){
this.board = []
this.status = "";
}
//method calls for an empty board filled with "E"
Board.prototype.initalize = function(){
$("td p").empty()
this.board = ["E","E","E","E","E","E","E","E","E"]
this.status = "New Game";
}
//return true if there is a win. Otherwise, false
Board.prototype.win = function(){
var B = this.board;
//check row
for (var i = 0; i <= 6; i = i + 3){
if (B[i] !== "E" && (B[i] === B[i+1]) && (B[i+1] === B[i+2])){
board.status = "Winner is: " + game.currentPlayer.name
return true
}
}
//check column
for (var i = 0; i <= 2 ; i++ ){
if (B[i] !== "E" && (B[i] === B[i+3]) && (B[i+3] === B[i+6])){
board.status = "Winner is: " + game.currentPlayer.name
return true
}
}
//check diagonal
for(var i = 0, j = 4; i <= 2 ; i = i + 2, j = j - 2) {
if(B[i] !== "E" && (B[i] == B[i + j]) && (B[i + j] === B[i + 2 * j]) ) {
board.status = "Winner is: " + game.currentPlayer.name
return true;
}
}
return false
}
//checks if the current status is draw. If so, updates the status to "Draw"
Board.prototype.draw = function(){
//checks if the board itself is draw
for(var i = 0; i < this.board.length ; i++){
if (this.board[i] === "E"){
return false;
}
}
board.status = "Draw!"
return true;
}
//method returns array of indexes that are not empty cells in the board
Board.prototype.available = function(){
var B = this.board;
var indexes = [];
for (var i = 0; i < B.length ; i ++){
if (B[i] === "E"){
indexes.push(i)
}
}
return indexes;
}
//checks first if the User Input is valid or not
Board.prototype.validMove = function(position){
var availableCells = this.available();
return availableCells.includes(position);
}
//updates the board when using jQuery click
//it will render the webpage by prototype_writeBoard
Board.prototype.updateBoard = function(position,playerInput) {
var availableCells = this.available();
if (availableCells.includes(position)){
this.board[position] = playerInput
}
};
function Game(firstPlayer){
this.currentPlayer = firstPlayer;
this.over = false;
this.win = "";
}
Game.prototype.switchPlayer = function(){
this.currentPlayer = (this.currentPlayer === human) ? computer : human
}
Game.prototype.play = function(){
$("td").click(function(){
var position = $(this).attr("id");
var positionNumber = parseInt(position.slice(4,5))
// This here renders to the board and updates board.board
if(board.validMove(positionNumber)){
//Checks if the move is valid. If it is, append it.
//Otherwise, alert the user that it is taken
$(this).find("p").append(game.currentPlayer.symbol)
board.updateBoard(positionNumber, game.currentPlayer.symbol)
//Check if it the game is over or draw
//If either is true, play new game
if (board.win() || board.draw()){
alert(board.status)
game.restart();
}
game.switchPlayer();
}else{
alert("Already taken")
}
})
}
Game.prototype.restart = function(){
board.initalize();
game.play()
}
})
body {
background: skyblue; }
#tictactoe {
max-width: 700px;
min-height: 300px;
margin: 68px auto;
display: flex;
width: 100%; }
#tictactoe table {
width: 100%;
font-size: 65px;
text-align: center;
vertical-align: middle;
table-layout: fixed; }
td {
height: 115px;
color: #101935;
background: #F2FDFF;
border: 5px solid #DBCBD8;
border-radius: 12px;
cursor: pointer;
transition: background 0.5s ease-out, color 0.5s ease-out; }
td:hover {
background: #564787;
color: #F2FDFF; }
.modal-dialog {
text-align: center; }
.modal-dialog .modal-footer {
text-align: center; }
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>TicTacToe FCC</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" href="css/styles.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</head>
<body>
<div id="tictactoe">
<table id="game-board">
<tbody>
<tr id="row1">
<td id="cell0"><p></p></td>
<td id="cell1"><p></p></td>
<td id="cell2"><p></p></td>
</tr>
<tr id="row2">
<td id="cell3"><p></p></td>
<td id="cell4"><p></p></td>
<td id="cell5"><p></p></td>
</tr>
<tr id="row3">
<td id="cell6"><p></p></td>
<td id="cell7"><p></p></td>
<td id="cell8"><p></p></td>
</tr>
</tbody>
</table>
</div>
<!--Modal Window -->
<div id="myModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Choose your character!</h4>
</div>
<div class="modal-body">
<p>Have fun!</p>
</div>
<div class="modal-footer">
<button type="button" id="xPlayer" class="btn btn-default" data-dismiss="modal">X</button>
<button type="button" id="oPlayer" class="btn btn-default" data-dismiss="modal">O</button>
</div>
</div>
</div>
</div>
</body>
<script src="js/javascript.js" type="text/javascript"></script>
</html>
In Game.play you are binding to the onclick of the td element. Then in Game.restart you are calling Game.play which is binding to the click event again. Since you are already calling Game.play when you setup the players there is no need to call it in Game.restart since it is still bound to the click event
Game.prototype.restart = function(){
board.initalize();
/* delete-me game.play() */
}
I have this form which has a button for file upload. When you select a file it shows at upload_prev div.
My problem is that when I try to select the same file nothing happens. I would like a validation or kind of non duplication function to run.
I did that. I tried many things and methods like using child nodes and seeing if the inner text is the same. I tried to loop using jquery each and getting the value, but all of them failed. I want to display a message that this file is already in the Box of upload_prev when I select it again.
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="https://code.jquery.com/jquery-1.10.1.js"></script>
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<link rel="stylesheet" type="text/css" href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<style type="text/css">
.fileUpload {
position: relative;
overflow: hidden;
margin: 10px;
}
.fileUpload input.upload {
position: absolute;
top: 0;
right: 0;
margin: 0;
padding: 0;
font-size: 20px;
cursor: pointer;
opacity: 0;
background-color: #fff;
filter: alpha(opacity=0);
}
.buttonwrap {
text-align: center;
padding-top: 20px;
float: left;
display: block;
}
.buttonsend:hover {
background-color: rgba(255, 255, 255, 0.2);
color: #225C51;
}
.buttonsend {
text-decoration: none;
color: #fff;
font-size: 18px;
padding-top: 5px;
padding-bottom: 5px;
padding-left: 10px;
padding-right: 10px;
background-color: rgba(72, 133, 130, .5);
}
span {
float: left;
display: flex;
width: 100%;
}
p.closed {
margin: 0 0 0 7px;
cursor: pointer;
}
</style>
<title></title>
<script type='text/javascript'>//<![CDATA[
$(window).load(function(){
// TO CLOSE THE SLECTED FILE
$(document).on('click', '.close', function() {
$(this).parents('span').remove();
})
//JUST TO PUT A VALUE IN THE BOX WHICH HAS
document.getElementById("uploadBtn").onchange = function() {
document.getElementById("uploadFile").value = this.value;
};
document.getElementById('uploadBtn').onchange = uploadOnChange;
//document.getElementById('uploadBtn').onchange = myFunction;
function uploadOnChange() {
var filename = this.value;
var lastIndex = filename.lastIndexOf("\\");
if (lastIndex >= 0) {
filename = filename.substring(lastIndex + 1);
}
// alert (filename);
var files = $('#uploadBtn')[0].files;
for (var i = 0; i < files.length; i++) {
$("#upload_prev").append('<span>' + '<div class="hesh">' + files[i].name +'</div>' + '<p class="close">X</p></span>');
}
document.getElementById('filename').value = filename;
document.getElementById("demo").innerHTML = files.length;
}
});//]]>
</script>
</head>
<body>
<FORM METHOD="post" ACTION="" ENCTYPE="multipart/form-data">
<!-- <input id="uploadFile" placeholder="Add files from My Computer" class="steptextboxq3" />-->
<div class="fileUpload btn btn-primary">
<span>Browse</span>
<input id="uploadBtn" type="file" class="upload" multiple="multiple" name="browsefile" />
</div>
<input id="filename" type="text" />
<div id="upload_prev"></div>
<div style="clear:both;"></div>
<div class="buttonwrap">
Send </div>
</FORM>
<p id="demo"></p>
</body>
</html>
This is my fiddle. How can I find a way to do this.
https://jsfiddle.net/Lc5gb7c9/
You can create an array to store files[i].name, use Array.prototype.indexOf() to check if file name has been added to array, if true call alert(), else add file name to array. You can reset array to [] at any point during process.
Note, <div> and <p> elements are not valid content of <span> element
// outside of `onchange`
var arr = [];
for (var i = 0; i < files.length; i++) {
if (arr.indexOf(files[i].name) === -1) {
arr.push(files[i].name)
$("#upload_prev").append('<div>'
+ '<div class="hesh">'
+ files[i].name + '</div>'
+ '<p class="close">X</p></div>');
} else {
alert(files[i].name + " already selected")
}
}
jsfiddle https://jsfiddle.net/Lc5gb7c9/2/
There is a low chance that a file with same name, size, type, modified time to repeat and have different content
const existingFiles = []
function findFile(file) {
return existingFiles.find(function(existingFile) {
return (
existingFile.name === file.name &&
existingFile.lastModified === file.lastModified &&
existingFile.size === file.size &&
existingFile.type === file.type
)
})
}
const input = document.querySelector('input')
input.addEventListener('change', function(e) {
const files = e.target.files
Array.from(files).forEach(function(file) {
const existingFile = findFile(file)
if (existingFile) {
console.error('Existing file: ', existingFile)
return
}
existingFiles.push(file)
console.warn('Added file: ', file)
})
})
<input type="file" />
I think you're running into issues with how your assigning your files to the dom. Remember FileLists are read only, meaning you can't select multiple files then keep going and append them to an existing element.
But you CAN append them to a JavaScript array:
files=[]; files.push(fileList);
So, I've edited your JSFiddle to include this functionality, as well as the check you were asking for:
https://jsfiddle.net/Lc5gb7c9/3/
I would recommend you look at:
http://blog.teamtreehouse.com/uploading-files-ajax for the way to upload via Ajax, as you'll need to create the formData object and then loop through your uploaded_files array and append them to the correct formData key. They are getting file from the html element, but you already have file in the uploaded_files array, so you would do it like:
for (var i = 0; i < uploaded_files.length; i++) {
formData.append('files[]', uploaded_files[i], uploaded_files[i].name);
}
Once that's done, you can make your ajax call.