Tic Tac Toe
Allows to people to play a game of Tic Tac Toe.
by Perry Kaufman
HTML
<h1>Tic Tac Toe</h1>
<h4>By Perry Kaufman</h4>
<div id="tic-tac-toe">
<div data-type="grid" class="tic-tac-toe-grid">
<div class="tic-tac-toe-cell" data-row="1" data-col="1" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="1" data-col="2" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="1" data-col="3" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="2" data-col="1" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="2" data-col="2" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="2" data-col="3" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="3" data-col="1" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="3" data-col="2" data-type="cell"></div>
<div class="tic-tac-toe-cell" data-row="3" data-col="3" data-type="cell"></div>
</div>
<div class="tic-tac-toe-symbols" data-type="controls">
<label class="tic-tac-toe-label">Player 1</label>
<div id="cross" class="symbol" data-type="symbol" data-symbol="cross">
<div class="cross"></div>
</div>
<label class="tic-tac-toe-label">Player 2</label>
<div id="nought" class="symbol" data-type="symbol" data-symbol="nought">
<div class="nought"></div>
</div>
</div>
<span data-type="message" class="tic-tac-toe-message"></span>
<button data-type="reset" class="tic-tac-toe-button">Restart</button>
</div>
CSS
:root {
--fore-color: #001564;
--back-color: #ededef;
}
* {
box-sizing: inherit;
}
html,
body {
background: var(--back-color);
box-sizing: border-box;
color: var(--fore-color);
font-family: sans-serif;
font-size: 20px;
}
h1, h4 {
margin: 0;
text-align: center;
}
h4 {
margin-bottom: 25px;
}
#tic-tac-toe {
display: grid;
grid-template-areas: "tr tl" "bl br";
justify-content: center;
}
.tic-tac-toe-grid {
display: grid;
grid-area: tr;
grid-template-columns: 100px 100px 100px;
grid-template-rows: 100px 100px 100px;
}
.tic-tac-toe-cell {
display: flex;
align-items: center;
justify-content: center;
}
[data-row="1"] {
border-bottom: 3px solid var(--fore-color);
}
[data-row="2"] {
border-bottom: 3px solid var(--fore-color);
border-top: 3px solid var(--fore-color);
}
[data-row="3"] {
border-top: 3px solid var(--fore-color);
}
[data-col="1"] {
border-right: 3px solid var(--fore-color);
}
[data-col="2"] {
border-right: 3px solid var(--fore-color);
border-left: 3px solid var(--fore-color);
}
[data-col="3"] {
border-left: 3px solid var(--fore-color);
}
.tic-tac-toe-label {
font-weight: bold;
}
.tic-tac-toe-symbols {
align-items: center;
border: 3px solid var(--fore-color);
border-radius: 5px;
display: flex;
flex-flow: column nowrap;
grid-area: tl;
justify-content: space-around;
padding: 5px;
margin: 10px;
}
.symbol {
align-items: center;
cursor: default;
display: flex;
height: 80px;
justify-content: center;
transition: all .25s ease-in-out;
width: 80px;
}
.symbol.small {
height: 10px !important;
width: 10px !important;
}
.cross {
height: 10px;
position: relative;
width: 80px;
}
.cross::before,
.cross::after {
background-color: var(--fore-color);
border-radius: 5px;
content: "";
height: 10px;
position: absolute;
width: 100%;
}
.cross::before {
transform: rotate(45deg);
}
.cross::after {
transform: rotate(-45deg);
}
.nought {
...
JavaScript
/*
* Can be added to an object to provide a simple listener interface.
*/
const listenerMixin = {
setListener(name, callback) {
if (typeof callback !== 'function') {
throw new Error('Error: callback must be a function');
}
if (!this._listeners) {
this._listeners = {};
}
this._listeners[name] = callback;
},
_triggerListener(name, ...args) {
if (this._listeners && this._listeners[name]) {
this._listeners[name].apply(this, args);
}
}
}
/*
* User Interface for a game of Tic Tac Toe.
*/
class TicTacToeView {
constructor() {
let container = document.querySelector('#tic-tac-toe');
let board = container.querySelector('[data-type="grid"]');
this._cells = board.querySelectorAll('[data-type="cell"]');
let selection = container.querySelector('[data-type="controls"]');
this._symbols = selection.querySelectorAll('[data-type="symbol"]');
this._message = container.querySelector('[data-type="message"]');
this._reset = container.querySelector('[data-type="reset"]');
this._init();
}
_init() {
//Initialize cells
this._cells.forEach((currentValue) => {
currentValue.ondragover = (event) => {
this._dragOverHandler(event);
};
currentValue.ondrop = (event) => {
this._dropHandler(event);
};
});
//Initialize symbols
this._symbols.forEach((currentValue) => {
currentValue.draggable = true;
currentValue.ondragstart = (event) => {
this._dragHandler(event);
};
});
this._reset.addEventListener('click', () => {
this._triggerListener('reset');
});
}
_dragHandler(event) {
event.dataTransfer.setData('text', event.target.id);
}
_dragOverHandler(event) {
event.preventDefault();
}
_dropHandler(event) {
event.preventDefault();
let id = event.dataTransfer.getData('text')
this._triggerListener('move', id, event.target.dataset.row, event.target.dataset.col)
}
...