So I want to make a tic tac toe game with html and script javascript, and my reset button doesn't seem to work, everything else seems to be working well, can anyone tell me what's wrong here?
I've tried moving the restart() function into the body, right after the button, and anywhere else I think possible, and changed the codes in the restart function to almost everything I think works and could be possible, but when I click the button nothing happens
<!DOCTYPE html>
<html>
<head>
<title>Tictactoe Game</title>
<style type="text/css">
[v-cloak] {
display: none;
}
td {
background: #DDD;
width: 50px;
height: 50px;
}
.piece-o {
margin: auto;
width: 30px;
height: 30px;
border: 3px solid #ff8080;
border-radius: 50%;
}
.piece-x {
margin: auto;
width: 30px;
height: 30px;
background: linear-gradient(45deg, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 43%,#0099ff 45%,#0099ff 55%,rgba(0,0,0,0) 57%,rgba(0,0,0,0) 100%), linear-gradient(135deg, rgba(0,0,0,0) 0%,rgba(0,0,0,0) 43%,#0099ff 45%,#0099ff 55%,rgba(0,0,0,0) 57%,rgba(0,0,0,0) 100%);
}
</style>
<script type="text/javascript">
function restart() {
for (i = 0; i <= 2; i++){
for (j = 0; j <= 2; j++){
this.game[i][j] = ' '
}
}
}
</script>
</head>
<body>
<div id="app" v-cloak>
<p>Current Player: <i :class="turn == 'O' ? 'far fa-circle' : 'fas fa-times'"></i></p>
<table>
<tr v-for="(row, rowKey, index) in game" :key="rowKey">
<td v-for="(col, colKey, index) in row" :key="colKey" #click="click(rowKey, colKey)">
<div v-if="col" :class="'piece-'+col.toLowerCase()"></div>
</td>
</tr>
</table>
</div>
<input type="button" onclick=restart() value="Restart">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script type="text/javascript">
var app = new Vue({
el: '#app',
data: {
turn: 'O',
game: [
['', '', ''],
['', '', ''],
['', '', '']
]
},
methods: {
click(row, col) {
if (this.game[row][col] != '') {
alert('Please select empty slot.')
return
}
this.game[row].splice(col, 1, this.turn);
this.calcResult()
},
calcResult() {
var game = this.game
var turn = this.turn
// Victory Condition
if ((this.game[0][0] == this.game[0][1] && this.game[0][0] == this.game[0][2] && this.game[0][0] != '')
|| (this.game[1][0] == this.game[1][1] && this.game[1][0] == this.game[1][2] && this.game[1][0] != '')
|| (this.game[2][0] == this.game[2][1] && this.game[2][0] == this.game[2][2] && this.game[2][0] != '')
|| (this.game[0][0] == this.game[1][0] && this.game[0][0] == this.game[2][0] && this.game[0][0] != '')
|| (this.game[0][1] == this.game[1][1] && this.game[0][1] == this.game[2][1] && this.game[0][1] != '')
|| (this.game[0][2] == this.game[1][2] && this.game[0][2] == this.game[2][2] && this.game[0][2] != '')
|| (this.game[0][0] == this.game[1][1] && this.game[0][0] == this.game[2][2] && this.game[0][0] != '')
|| (this.game[0][2] == this.game[1][1] && this.game[0][2] == this.game[2][0] && this.game[0][2] != '')
) {
alert('Player ' + this.turn + ' is the winner!');
return;
}
// Draw condition
if (this.game[0][0] != '' && this.game[0][1] && this.game[0][2] && this.game[1][0] && this.game[1][1]
&& this.game[1][2] && this.game[2][0] && this.game[2][1] && this.game[2][2]) {
alert('Draw!');
return;
}
// Next player turn
this.turn = this.turn == 'O' ? 'X' : 'O'
}
}
})
</script>
</body>
</html>
You can change code like this.
<div id="app" v-cloak>
...
</table>
<input type="button" #click="restart()" value="Restart">
</div>
And add restart function to Vue methods because you have to use "game" data of Vue.
...
},
restart() {
for (i = 0; i <= 2; i++){
for (j = 0; j <= 2; j++){
this.game[i][j] = ''
}
}
turn = 'O';
alert("Restart");
}
I imagine it's because your restart() function is outside of the scope of your Vue application. I'm not familiar with Vue, but you probably need to have restart() be a function within app.methods, and call it just like you're calling the other Vue methods. Which also means you'd probably have to have your restart button within the <div id="app" v-cloak> element. You've placed it outside of the parent Vue element at the moment.
Related
I tried to create a tic-tac-toe game using minimax algorithm. But before I could write the whole algorithm, I encountered an absurd type of error. This code is for reference in case if you have to look into the full code.
//Using minimax to the same code of tictactoe2.js
$ = document.querySelectorAll(".box");
// adding click event to all boxes
$.forEach((element)=>{
element.addEventListener("click", mainFunction);
})
document.querySelector(".reset-button-container").addEventListener("click", reset);
function mainFunction(e){
if (checkForGameOver() === false){ //once game is over, so should be all clicks
if (e.target.innerText !== ""){
alert("Click Another Square!!!");
}
else{
e.target.innerText = "X";
if (checkForGameOver() === false){ //computer gets its turn only if the game isn't over after players turn
setTimeout(computerTurn, 500);
setTimeout(checkForGameOver, 501); //501 is provided to run checkForGameOver only after computerTurn
}
}
}
}
//a function that randomly plays computers turn
function computerTurn(){
let board = [], bestScore = Infinity, bestMove = {}, k = 0;
//array of innerTexts of each box is the board in one dimension
$.forEach((element, index) => {
board[index] = element.innerText;
});
let board2D = convertToTwoDimension(board, 3, 3); //convert 1d board to 2d of 3 rows and 3 columns
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
if (board2D[i][j] === ""){
board2D[i][j] = "O";
let score = minimax(board2D, false);
board2D[i][j] = "";
if(score < bestScore){
bestScore = score;
bestMove = {i, j};
}
}
}
}
board2D[bestMove.i][bestMove.j] = "O"; //assigning bestMove to baord2D
//to dispaly the played bestMove by the computer on screen
for(i = 0; i < 3; i++){
for(j = 0; j < 3; j++){
$[k].innerText = board2D[i][j];
k += 1;
}
}
}
//minimax implementation
function minimax(position, maximizing){
console.log(position);
//if the game is over at that position, return the score based on who is wining
if (checkForGameOver() === true){
if(document.querySelector(".win-message").innerText[0] === "X"){
return 1; //X (maximizing player) wins
}
else if(document.querySelector(".win-message").innerText[0] === "O"){
return -1; //O (minimizing player) wins
}
else{
return 0; // the game is a draw
}
}
}
//function to check if game is over. But I think it can be written more easily.
function checkForGameOver(){
var tempText = [];
for(i = 1; i <= 9; i++){
tempText[i] = $[i-1].innerText;
}
if (tempText[1] === tempText[2] && tempText[2] === tempText[3] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[4] === tempText[5] && tempText[5] === tempText[6] && tempText[4] !== ""){
document.querySelector(".win-message").innerText = tempText[4] + " Wins!!";
return true;
}
else if (tempText[7] === tempText[8] && tempText[8] === tempText[9] && tempText[7] !== ""){
document.querySelector(".win-message").innerText = tempText[7] + " Wins!!";
return true;
}
else if (tempText[1] === tempText[4] && tempText[4] === tempText[7] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[2] === tempText[5] && tempText[5] === tempText[8] && tempText[2] !== ""){
document.querySelector(".win-message").innerText = tempText[2] + " Wins!!";
return true;
}
else if (tempText[3] === tempText[6] && tempText[6] === tempText[9] && tempText[3] !== ""){
document.querySelector(".win-message").innerText = tempText[3] + " Wins!!";
return true;
}
else if (tempText[1] === tempText[5] && tempText[5] === tempText[9] && tempText[1] !== ""){
document.querySelector(".win-message").innerText = tempText[1] + " Wins!!";
return true;
}
else if (tempText[3] === tempText[5] && tempText[5] === tempText[7] && tempText[3] !== ""){
document.querySelector(".win-message").innerText = tempText[3] + " Wins!!";
return true;
}
//if none of above, which is for win only, is true, check for draw which can be done by filtering the remaining blank squares. If no squares are lest, then game is a draw else the game isn't over yet.
else{
arrayOfBlankCharacters = tempText.filter(element => {
return (element === "");
});
if (arrayOfBlankCharacters.length === 0){
document.querySelector(".win-message").innerText = "Game Drawn!!";
return true;
}
else{
return false;
}
}
}
//a function that resets the board and win message.
function reset(){
for(i = 1; i <= 9; i++){
$[i-1].innerText = "";
}
document.querySelector(".win-message").innerText = "";
}
//function to convert 1d array to 2d array => useful for computerTurn() function to access board in 2d view
function convertToTwoDimension(initialArray, rows, columns){
var k = 0, finalArray = [];
for(i = 0; i < initialArray.length/columns; i++){
finalArray[i] = []; //state that each element of finalArray is also an array
for(j = 0; j < initialArray.length/rows; j++){
finalArray[i][j] = initialArray[k];
k += 1;
}
}
return finalArray;
}
#import url('https://fonts.googleapis.com/css2?family=Roboto:wght#900&display=swap');
:root{
font-size: 16px;
font-family: 'Roboto', sans-serif;
}
*, *::after, *::before{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.description-header{
background-color: lightgrey;
line-height: 3rem;
padding-left: 2rem;
margin-bottom: 2rem;
}
.player-information{
text-align: center;
margin-bottom: 1rem;
}
.player-information > h3{
font-size: 2.5rem;
}
.player-information > h3 > span{
color:brown;
}
.game-box-container{
display: flex;
flex-direction: column;
min-height: 100vh;
align-items: center;
}
.tictactoe-box{
height: 250px;
width: 250px;
display: grid;
grid-template: repeat(3, 1fr)/repeat(3, 1fr);
grid-gap: 0.5rem;
background-color: black;
}
.reset-button-container{
margin-top: 1rem;
background: lightgrey;
border-radius: 0.5rem;
}
.tictactoe-box > .box{
display: flex;
justify-content: center;
align-items: center;
font-size: 4rem;
background-color: white;
cursor: context-menu
}
.reset-button-container > span{
display: block;
font-size: 2rem;
padding: 0.5rem 1.5rem;
}
.reset-button-container:hover{
background-color: grey;
cursor: context-menu;
}
.win-message{
margin-top: 1rem;
width: 25%;
font-size: 2.5rem;
background-color: lightgrey;
text-align: center;
border-radius: 10px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>TicTacToe</title>
<link rel="stylesheet" type="text/css" href="tictactoe.css">
<link rel="icon" type="image/x-icon" href="https://upload.wikimedia.org/wikipedia/commons/thumb/3/32/Tic_tac_toe.svg/1024px-Tic_tac_toe.svg.png">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-sclae=1.0">
<meta name="description" content="">
<meta name="keywords" content="">
</head>
<body>
<header class="description-header">
<h3>My TicTacToe Game</h3>
</header>
<div class="player-information">
<h3>Player: "<span>X</span>"; Computer: "<span>O</span>"</h3>
<h3>Player First</h3>
</div>
<div class="game-box-container">
<div class="tictactoe-box">
<div class="box box1"></div>
<div class="box box2"></div>
<div class="box box3"></div>
<div class="box box4"></div>
<div class="box box5"></div>
<div class="box box6"></div>
<div class="box box7"></div>
<div class="box box8"></div>
<div class="box box9"></div>
</div>
<div class="reset-button-container">
<span>Reset</span>
</div>
<div class="win-message">
</div>
</div>
<script type="text/javascript" src="usingMinimax.js"></script>
</body>
</html>
My problem arises in the minimax(position, maximizing) function of the code as below. console.log(position) runs just fine, but because of the checkForGameOver() in the below function, it throws an error that board2D[i] is undefined which is unexpected because there is no any arguments passed in to the checkForGameOver() function. As soon as I remove the (checkForGameOver() === false) condition and run it, the computer makes a move for the first available spot which is as expected.
//minimax implementation
function minimax(position, maximizing){
console.log(position);
//if the game is over at that position, return the score based on who is wining
if (checkForGameOver() === true){
if(document.querySelector(".win-message").innerText[0] === "X"){
return 1; //X (maximizing player) wins
}
else if(document.querySelector(".win-message").innerText[0] === "O"){
return -1; //O (minimizing player) wins
}
else{
return 0; // the game is a draw
}
}
}
A function that doesn't take any variable as arguments just returns a boolean value, is somehow the cause of an error that the variable is undefined.
this is my first question on stackoverflow.
I am learning about javascript and decided to start a project.
I'm making a scoreboard to keep track of the score during table tennis. I managed to make this work and decided to add some features like a match history and show which player has to serve.
However, I'm stuck with the ReferenceError. In most other questions about this, people just forgot to add the variable or it had something to do with jquery. I don't think that's my problem.
In table tennis the player with serve changes every 2 points. I decided to add scorePlayer1 and scorePlayer2 to make a totalScore. When this is divided by 2, I can check if this is an integer, and if it is, the player with serve changes. However, whatever I try, the variable totalScore is not defined.
I learned HTML/CSS first at w3schools.com and later used it to learn javascript.
I have pasted the code into multiple syntax checkers, but got no errors.
The button is there to pick the serve player. Then, I want to swith the right to serve to the opposite player after 2 points are scored. I tried this with function changeServePlayer. However, when I try this in Chrome and type in the console: totalScore, it returns the Uncaught ReferenceError. Why does this happen or is there a better way to achieve the goal?
Here's code I used:
var currentScorePlayerOne = 0;
var currentScorePlayerTwo = 0;
var currentServePlayer;
var totalScore;
window.addEventListener("keyup", checkKeyPress);
function checkKeyPress(key) {
if (key.keyCode == "90" && currentScorePlayerOne != 0) { //Z
document.getElementById('scorePlayerOne').innerHTML = --currentScorePlayerOne;
changeServePlayer();
changeServeIcon();
}
if (key.keyCode == "88") { //X
document.getElementById('scorePlayerOne').innerHTML = ++currentScorePlayerOne;
changeServePlayer();
changeServeIcon();
}
if (key.keyCode == "78" && currentScorePlayerTwo != 0) { //N
document.getElementById('scorePlayerTwo').innerHTML = --currentScorePlayerTwo;
changeServePlayer();
changeServeIcon();
}
if (key.keyCode == "77") { //M
document.getElementById('scorePlayerTwo').innerHTML = ++currentScorePlayerTwo;
changeServePlayer();
changeServeIcon();
}
updateSet();
}
function updateSet() {
if (currentScorePlayerOne > 10 && currentScorePlayerOne > currentScorePlayerTwo + 1) {
resetScores();
}
if (currentScorePlayerTwo > 10 && currentScorePlayerTwo > currentScorePlayerOne + 1) {
resetScores();
}
}
function resetScores() {
currentScorePlayerOne = 0;
currentScorePlayerTwo = 0;
document.getElementById('scorePlayerOne').innerHTML = currentScorePlayerOne;
document.getElementById('scorePlayerTwo').innerHTML = currentScorePlayerTwo;
}
function changeServePlayer() {
totalScore = currentScorePlayerOne + currentScorePlayerTwo;
Number.isInteger(totalScore / 2);
if (Number.isInteger == true && totalScore != 0 && currentServePlayer == 1) {
currentServePlayer = 2;
}
if (Number.isInteger == true && totalScore != 0 && currentServePlayer == 2) {
currentServePlayer = 1;
}
}
function changeServeIcon() {
if (currentServePlayer == 1) {
document.getElementById('serveP1').style.opacity = "1";
document.getElementById('serveP2').style.opacity = "0.2";
} else {
document.getElementById('serveP2').style.opacity = "1";
document.getElementById('serveP1').style.opacity = "0.2";
}
}
<!DOCTYPE html>
<html>
<head>
<script src="Scoreboard1javascript.js"></script>
</head>
<body>
<button type="button" onclick="chooseServingPlayer()">
Serve
</button>
<script>
var randomServeNumber;
function chooseServingPlayer() {
if (currentScorePlayerOne == 0 && currentScorePlayerTwo == 0) {
document.getElementById('serveP1').style.opacity = "0.2";
document.getElementById('serveP2').style.opacity = "0.2";
randomServeNumber = Math.floor(Math.random() * 10);
if (randomServeNumber > 5) {
currentServePlayer = 1;
changeServeIcon();
} else {
currentServePlayer = 2;
changeServeIcon();
}
}
}
function changeServeIcon() {
if (currentServePlayer == 1) {
document.getElementById('serveP1').style.opacity = "1";
document.getElementById('serveP2').style.opacity = "0.2";
} else {
document.getElementById('serveP2').style.opacity = "1";
document.getElementById('serveP1').style.opacity = "0.2";
}
}
</script>
<nav>
<img src="tafeltennisbat.png" alt="serve" style="width: 50px; height: 50px; opacity: 0.2" id="serveP1"> Score P1
</nav>
<nav>
Score P2
<img src="tafeltennisbat.png" alt="serve" style="width: 50px; height: 50px; opacity: 0.2" id="serveP2">
</nav>
<nav id="scorePlayerOne" style="font-size: 50px">
0
</nav>
<nav id="scorePlayerTwo" style="font-size: 50px">
0
</nav>
</body>
</html>
I forgot to check which file I was referencing in my html script. I was referencing an old version of the javascript, which made every change I made in javascript useless.
It can be really that stupid sometimes...
I am doing an assignment tic tac toe game. My minimax algorithm works fine on its own with an array and two players provided but when working with html collections of divs it gives an error of maximum call stack size exceed. I don't understand why it is happening. By searching google I know that my function somewhere calling itself over and over again but can't figure it out what to do solve this issue. I tried converting the .cells div collection into an array but still the same error occurred. Anybody please help me with this.
Thank you.
var humanPlayer, aiPlayer;
humanPlayer = 'x';
aiPlayer = 'o';
var cells = document.querySelectorAll(".cell");
/** Tic Tac Toe game object **/
var TicTacToe = {
checkWinner : function(arr, player){
if(
(arr[0] === player && arr[1] === player && arr[2] === player)||
(arr[3] === player && arr[4] === player && arr[5] === player)||
(arr[6] === player && arr[7] === player && arr[8] === player)||
(arr[0] === player && arr[3] === player && arr[6] === player)||
(arr[1] === player && arr[4] === player && arr[7] === player)||
(arr[2] === player && arr[5] === player && arr[8] === player)||
(arr[0] === player && arr[4] === player && arr[8] === player)||
(arr[2] === player && arr[4] === player && arr[6] === player)
){
return true;
}
else{
return false;
}
},//checkWinner function.
getAvailableSpots : function(arr){
var spots = [];
for(var i = 0; i < arr.length; i++){
if(arr[i].textContent !== "x" && arr[i].textContent !== "o"){
spots.push(i);
}
}
return spots;
},// getAvailableSpots function.
minimax : function(newBoard, player){
newBoard = Array.from(newBoard);
var availSpots = this.getAvailableSpots(newBoard);
if(this.checkWinner(newBoard, aiPlayer)){
return {score: 10};
}
else if(this.checkWinner(newBoard, humanPlayer)){
return {score: -10};
}
else if(availSpots.length === 0){
return {score: 0};
}
var moves = [];
for(var j = 0; j < availSpots.length; j++){
var move = {};
move.index = availSpots[j];
newBoard[availSpots[j]] = player;
if(player === aiPlayer){
var result1 = this.minimax(newBoard, humanPlayer);
move.score = result1.score;
}
else{
var result2 = this.minimax(newBoard, aiPlayer);
move.score = result2.score;
}
newBoard[availSpots[j]] = move.index;
moves.push(move);
}
var bestMove;
if(player === aiPlayer){
var bestScore1 = -100000;
for(var k = 0; k < moves.length; k++){
if(moves[k].score > bestScore1){
bestScore1 = moves[k].score;
bestMove = k;
}
}
}
else{
var bestScore2 = 100000;
for(var l = 0; l < moves.length; l++){
if(moves[l].score < bestScore2){
bestScore2 = moves[l].score;
bestMove = l;
}
}
}
return moves[bestMove];
}// minimax function.
};//TicTacToe object literal.
function move(e){
if(e.target.className === "cell" && e.target.textContent === ""){
e.target.textContent = humanPlayer;
var result = TicTacToe.minimax(cells, aiPlayer);
cells[result.index].textContent = aiPlayer;
}
}
document.querySelector('.cells').addEventListener('click', move);
.*{
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.cells {
width: 390px;
height: 390px;
margin: 50px auto;
}
.cell {
width:128px;
height: 128px;
border: 1px solid #dedede;
float: left;
text-align: center;
line-height: 128px;
font-size: 80px;
font-weight: bold;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="cells">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
</body>
</html>
It's because your minmax function is recursive, meaning it tries every possible combination chain. To not repeat the same chain over and over, it creates a clone of the board on every recursion (via Array.from(newBoard)).
Now, back when you were testing it with primitve arrays, that copying of the board worked fine. However, now your board array is actually containing DOM objects, which will not be cloned by Array.from(). So your AI is basically altering the cells for every attempt which causes a stack overflow.
My suggestion would be to convert the current board to a primitive array before passing it to the minmax function.
var humanPlayer, aiPlayer;
humanPlayer = 'x';
aiPlayer = 'o';
var cells = document.querySelectorAll(".cell");
/** Tic Tac Toe game object **/
var TicTacToe = {
checkWinner : function(arr, player){
if(
(arr[0] === player && arr[1] === player && arr[2] === player)||
(arr[3] === player && arr[4] === player && arr[5] === player)||
(arr[6] === player && arr[7] === player && arr[8] === player)||
(arr[0] === player && arr[3] === player && arr[6] === player)||
(arr[1] === player && arr[4] === player && arr[7] === player)||
(arr[2] === player && arr[5] === player && arr[8] === player)||
(arr[0] === player && arr[4] === player && arr[8] === player)||
(arr[2] === player && arr[4] === player && arr[6] === player)
){
return true;
}
else{
return false;
}
},//checkWinner function.
getAvailableSpots : function(arr){
var spots = [];
for(var i = 0; i < arr.length; i++){
if(arr[i] !== "x" && arr[i] !== "o"){
spots.push(i);
}
}
return spots;
},// getAvailableSpots function.
minimax : function(newBoard, player){
newBoard = Array.from(newBoard);
var availSpots = this.getAvailableSpots(newBoard);
if(this.checkWinner(newBoard, aiPlayer)){
return {score: 10};
}
else if(this.checkWinner(newBoard, humanPlayer)){
return {score: -10};
}
else if(availSpots.length === 0){
return {score: 0};
}
var moves = [];
for(var j = 0; j < availSpots.length; j++){
var move = {};
move.index = availSpots[j];
newBoard[availSpots[j]] = player;
if(player === aiPlayer){
var result1 = this.minimax(newBoard, humanPlayer);
move.score = result1.score;
}
else{
var result2 = this.minimax(newBoard, aiPlayer);
move.score = result2.score;
}
newBoard[availSpots[j]] = move.index;
moves.push(move);
}
var bestMove;
if(player === aiPlayer){
var bestScore1 = -100000;
for(var k = 0; k < moves.length; k++){
if(moves[k].score > bestScore1){
bestScore1 = moves[k].score;
bestMove = k;
}
}
}
else{
var bestScore2 = 100000;
for(var l = 0; l < moves.length; l++){
if(moves[l].score < bestScore2){
bestScore2 = moves[l].score;
bestMove = l;
}
}
}
return moves[bestMove];
}// minimax function.
};//TicTacToe object literal.
function move(e){
if(e.target.className === "cell" && e.target.textContent === ""){
e.target.textContent = humanPlayer;
var result = TicTacToe.minimax(domBoardToArray(cells), aiPlayer);
cells[result.index].textContent = aiPlayer;
}
}
function domBoardToArray(cells){
return Array.prototype.slice.call(cells).map(function (cell){
return cell.textContent
})
}
document.querySelector('.cells').addEventListener('click', move);
.*{
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.cells {
width: 390px;
height: 390px;
margin: 50px auto;
}
.cell {
width:128px;
height: 128px;
border: 1px solid #dedede;
float: left;
text-align: center;
line-height: 128px;
font-size: 80px;
font-weight: bold;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div class="cells">
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
<div class="cell"></div>
</div>
</body>
</html>
Problem Statement:
I am creating IOS App from Angularjs using ng-cordova plugin,For my project i am using horizontal scroller(like Swipe),Whenever i am trying to select/Deselect the images it's response is getting delay.I used $watch() to watch the changes for my $scope object.I am not able to identify where the problem is raising whether in my css either in my controller.Please any one can help me to clear the issue.
Markup:
<div class="wrapper no-copy">
<div class="internal" ng-repeat="pf in printlist"><img class="" ng-click="pf.selectFile = !pf.selectFile ;showCustom($event,pf)" ng-src="{{pf.imagePath}}"><div class="no-copy filedetail"><div style="color:black;margin-left:7px;" class=" no-copy internal font-filedetail">{{pf.filename.substring(0,8)}}..</br>Pages: {{pf.totalPages}}</br>{{pf.releaseCode}}</div><div class="internal filedetailbar"><img style="height:70px;" src="images/beacon/line_icon.png"></div></div>
</div>
</div>
CSS:
.wrapper {
width: 100%;
white-space: nowrap;
overflow-y: hidden;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
padding: 1rem;
background-color: white;
// Toggle this depending upon whether you want to see the scrollbar
&::-webkit-scrollbar {
display: none;
}
}
.internal {
display: inline;
}
Controller:
$scope.showCustom = function (event, detail) {
console.log('detail')
console.log(detail)
/*$mdDialog.show({
locals: {name: detail},
clickOutsideToClose: true,
scope: $scope,
preserveScope: true,
templateUrl: 'ibprintProcessList/filedetail.html',
controller: function DialogController($scope, $mdDialog, name) {*/
$scope.del = detail;
console.log($scope.del.color)
console.log('*************')
console.log($scope.del)
if (!$scope.del.color) {
$scope.color = false
}
if ($scope.del.selectFile) {
$scope.filestatus = 'selected '
$scope.disabled = false
$scope.isFileSelect = $scope.isFileSelect + 1
$scope.selectfileextension = $scope.del.filename.split('.')
console.log('???????????????')
console.log($scope.selectfileextension[1])
if (($scope.selectfileextension[1] == 'pdf') || ($scope.selectfileextension[1] == 'rtf')) {
$scope.del.imagePath = "./images/beacon/pdf_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'doc') || ($scope.selectfileextension[1] == 'docx')) {
$scope.del.imagePath = "./images/beacon/word_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'xls') || ($scope.selectfileextension[1] == 'xlsx')) {
$scope.del.imagePath = "./images/beacon/xls_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'ppt') || ($scope.selectfileextension[1] == 'pptx')) {
$scope.del.imagePath = "./images/beacon/pp_icon_select.png"
}
else if (($scope.selectfileextension[1] == 'png') || ($scope.selectfileextension[1] == 'jpg') || ($scope.selectfileextension[1] == 'jpeg') || ($scope.selectfileextension[1] == 'bmp') || ($scope.selectfileextension[1] == 'gif') || ($scope.selectfileextension[1] == 'tiff') || ($scope.selectfileextension[1] == 'tif')) {
$scope.del.imagePath = "./images/beacon/image_icon_select.png"
}
}
else {
$scope.disabled = true
$scope.isFileSelect = $scope.isFileSelect - 1
$scope.filestatus = 'unselected '
$scope.selectfileextension = $scope.del.filename.split('.')
if (($scope.selectfileextension[1] == 'pdf') || ($scope.selectfileextension[1] == 'rtf')) {
$scope.del.imagePath = "./images/beacon/pdf_icon.png"
}
else if (($scope.selectfileextension[1] == 'doc') || ($scope.selectfileextension[1] == 'docx')) {
$scope.del.imagePathh = "./images/beacon/word_icon.png"
}
else if (($scope.selectfileextension[1] == 'xls') || ($scope.selectfileextension[1] == 'xlsx')) {
$scope.del.imagePath = "./images/beacon/xls_icon.png"
}
else if (($scope.selectfileextension[1] == 'ppt') || ($scope.selectfileextension[1] == 'pptx')) {
$scope.del.imagePath = "./images/beacon/pp_icon.png"
}
else if(($scope.selectfileextension[1] == 'png') || ($scope.selectfileextension[1] == 'jpg') || ($scope.selectfileextension[1] == 'jpeg') || ($scope.selectfileextension[1] == 'bmp') || ($scope.selectfileextension[1] == 'gif') || ($scope.selectfileextension[1] == 'tiff') || ($scope.selectfileextension[1] == 'tif')){
$scope.del.imagePath = "./images/beacon/image_icon.png"
}
}
/*$scope.closeDialog = function () {
$mdDialog.hide();
//$scope.printNow()
}*/
$scope.del.$digest()
$scope.printlist.$digest()
console.log('Watch function starts')
// $scope.$watch($scope.del, $scope.showCustom);
$scope.$watchGroup(['$scope.del', '$scope.printlist'],$scope.showCustom,true)
console.log('Watch function ends')
}
When the user Select/Deselect the $scope.showCustom(event,pf) is triggered
pf object is passed to controller and assigned the pf object to $scope.del object
When the user Select/Deselect the $scope.del.imagepath holds the images and changed the image source
Referimage: Screen Shot
I tried both $watch() and $watchGroup() functions it's getting delay on Select/Deselect
Just use a simple horizontal scroller using overflow style,It will work as a SWIPE on your mobile app.
-webkit-overflow-scrolling: touch;
&::-webkit-scrollbar {
display: none;
}
removed the above CSS ,beacause you are porting from web to mobile app. -webkit-overflow-scrolling is for browser. sometimes it may/maynot work while you converting your web app to mobile app.
Instead that add the below CSS:
overflow-y: hidden;
overflow-x: scroll;
width: 500px; // you can change the width size depends on you
white-space: nowrap;
I'm trying to implement a 'Tag Editor' field in my app, the same way as SO does.
Right now i got this:
EDIT: I have coded this into a jQuery plugin at:
https://github.com/fernandotenorio/tagme.git
Fiddle
http://jsfiddle.net/FernandoTen/PnYuF/
html
<div style="margin-left: auto; margin-right: auto; width: 400px;">
<span style='color: #333; font-size: small'>
Tags [a-z A-Z 0-9 # + . -]
</span>
<div id='tags_container'>
<span id='tags_queue'></span>
<input type='text' id='tf' />
</div>
</div>
js
$(document).ready(function () {
var rexp = /[^a-zA-Z0-9#+\.\-]/
var left = 37;
var right = 39;
var del = 8;
var space = 32;
var comma = 188;
var minlen = 3;
var maxlen = 15;
var current_tag = null
$('#tf').focus().attr('maxlength', maxlen)
$('#tags_container').click(function () {
$('#tf').focus()
})
$('#tf').keydown(function (e) {
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
var txt = $(this).val().trim()
// handle navigation between tags
if ((code === left || code === right) && txt.length === 0) {
if (!current_tag)
{
if (code === left)
current_tag = $('#tags_queue span').last()
else
current_tag = $('#tags_queue span').first()
if (current_tag.length > 0) current_tag.css('background-color', '#9FC2D6')
} else //current tag exists
{
if (code === left)
current_tag = current_tag.css('background-color', '#B8D0DE').prev()
else
current_tag = current_tag.css('background-color', '#B8D0DE').next()
if (current_tag.length > 0)
current_tag.css('background-color', '#9FC2D6')
// hit last/first, clean current_tag
else
{
current_tag.css('background-color', '#B8D0DE')
current_tag = null
}
}
}
});
$('#tf').keypress(function (e) {
var token = String.fromCharCode(e.which)
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
if (token.match(rexp) && (code !== del) && (code !== left) && (code !== right))
return false;
});
$('#tf').keyup(function (e) {
var code = e.keyCode ? e.keyCode : e.which ? e.which : e.charCode;
var txt = $(this).val().trim()
if (code === del && txt.length === 0 && current_tag) {
current_tag.remove()
current_tag = null
return
}
// clean current_tag, user is typing
if (current_tag && (code !== left) && (code != right))
{
current_tag.css('background-color', '#B8D0DE')
current_tag = null
}
if (txt.length > maxlen) txt = txt.substring(0, maxlen)
if (txt.match(rexp)) {-
$(this).val("")
return
} else if (code === space || code === comma) {
if (txt.length < minlen) return;
var tag = $('<span></span>', {
'class': 'tag'
}).html(txt).append($('<a></a>', {
'class': 'close_tag',
html: '×',
click: function () {
$(this).parent().fadeOut(function(){
$(this).remove()
current_tag.css('background-color', '#B8D0DE')
current_tag = null
})
}
}))
$('#tags_queue').append(tag)
$(this).val("")
}
});
})
css
div#tags_container {
border: 1px solid #ccc;
padding-bottom: 5px;
border-radius: 5px;
background-color: beige;
overflow: hidden;
}
input#tf {
border: 1px solid orange !important;
outline: none !important;
background-color: transparent !important;
height: 30px;
margin-top: 2px;
padding-left: 5px;
}
a.close_tag {
margin-left: 5px;
color: #fff;
}
a.close_tag:hover {
cursor: pointer;
}
span.tag {
background-color: #B8D0DE;
color: #111;
margin-left: 5px;
margin-top: 5px;
padding: 5px;
float: left;
border-radius: 5px;
}
span.tag:hover {
background-color: #9FC2D6;
}
span#tags_queue {
margin-right: 5px;
vertical-align: middle;
}
The problem is, how can i handle the 'hidden' content using the arrows keys, the way SO does? It looks like i have to handle the cursor position inside the text-fields, and watch for arrow-left and arrow-right events...and there's also the click on the completed tags.
I can think of the following (anti-SO design) solutions:
remove white-space: nowrap from the tags_container div, and let
the div 'grow' as the user add more tags.
Add overflow: scroll to the tags_container div (very ugly!)
So, i appreciate any new ideas on how to approximate the SO tag-editor behaviour.
Thanks!
Reinvent the wheel is a bad practice : take a look at the excellent Tag-it jQuery plugin code, it implements the stuff you are working on.