DEV Community

ozboware
ozboware

Posted on

One way to make Tic Tac Toe using Javascript

There are quite a few posts online detailing how to make Tic Tac Toe using Javascript and other languages. This isn't meant to be a definitive "how to" on the subject, it's more of a "how I did it".

First I built the HTML layout and style

<div id="board">
    <div class="box" data-ida="0" data-idb="0"></div>
    <div class="box" data-ida="0" data-idb="1"></div>
    <div class="box" data-ida="0" data-idb="2"></div>
    <div></div>
    <div class="box" data-ida="1" data-idb="0"></div>
    <div class="box" data-ida="1" data-idb="1"></div>
    <div class="box" data-ida="1" data-idb="2"></div>
    <div></div>
    <div class="box" data-ida="2" data-idb="0"></div>
    <div class="box" data-ida="2" data-idb="1"></div>
    <div class="box" data-ida="2" data-idb="2"></div>
    <div id="resetBtn">Reset board</div>
</div>
Enter fullscreen mode Exit fullscreen mode

By itself, it's not very impressive, just "Reset board" plonked onto the screen. It needs some style

html, body{
    font-family: arial;
}

#board{
    text-align:center;
    width: 466px;
    position: absolute;
    left: calc(50% - 338px);
    top: 35px;
}

.box{
    border: 1px solid;
    width: 150px;
    height: 150px;
    display: inline-block;
    vertical-align: top;
    cursor: pointer;
}

#resetBtn{
    margin-top: 10px;
    padding: 15px;
    background-color: green;
    width: 100px;
    position: absolute;
    left: calc(50% - 65px);
    color: #fff;
    font-weight: bold;
    cursor: pointer;
    border-radius: 15px;
    box-shadow: 3px 3px lightgrey;
    transition: .3s;
}

#resetBtn:active{
    margin-top: 12px;
    left: calc(50% - 63px);
    box-shadow: 1px 1px lightgrey;
}
Enter fullscreen mode Exit fullscreen mode

Self explanatory for the board and the boxes. With the button, I added a shadow then used the :active property, which is the CSS equivalent of "onclick", to reduce the shadow and shift the button down-right a couple of pixels to give the impression it's being pushed. Now to add in the score board, so back to the HTML

<div id="score_board">
    <div id="sbhead">SCORE</div>
    <div class="score_block">
        <div class="sblhead">O</div>
        <div class="sblbody" data-id="o">0</div>
    </div>
    <div class="score_block">
        <div class="sblhead">X</div>
        <div class="sblbody" data-id="x">0</div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Again, not very impressive by itself, let's spruce it up with some style

#score_board{
    border: 1px solid;
    width: 200px;
    height: 453px;
    top: 35px;
    margin-left: 467px;
    position: absolute;
    text-align: center;
    font-weight: bold;
}

#sbhead{
    padding: 7px 0;
    font-size: 20px;
    border-bottom: 1px solid;
}

.score_block{
    border-bottom: 1px solid;
    height: calc(50% - 20px);
}

.sblhead{
    padding: 5px;
    border: 1px solid;
    font-size: 32px;
}

.sblbody{
    font-size: 114px;
}
Enter fullscreen mode Exit fullscreen mode

It might be worth noting at this point that when I'm doing the styling, I'm using the developer console built into the browser so I have a live view of the changes. For the most part I use Brave browser so I either press F12 or right click an element and click inspect, then I edit away at the styles for each element before copying them to my style sheet.

Finally I added the notifications element which will display the winning combination.

<div id="notification"></div>
Enter fullscreen mode Exit fullscreen mode

and its style

#notification{
    position: absolute;
    text-align: center;
    top: 560px;
    font-size: 50px;
    margin-right: 209px;
}
Enter fullscreen mode Exit fullscreen mode

Looking good so far. For the sake of aesthetics, I wrapped it all in a container and centered it on the screen. I won't add it here, we'll see that in the final script at the bottom. So now the template is ready, it's time to add in the Javascript functionality.

First things first, I'll begin with the startGame function

function startGame(){
    let player = 'O';
    document.getElementById('notification').innerHTML = '';
    var x = [['0','0','0'],['0','0','0'],['0','0','0']];
    let box = document.getElementsByClassName('box');

    var clickEvent = function() {
        let ida = this.getAttribute('data-ida');
        let idb = this.getAttribute('data-idb');
        if(this.innerText == ''){
            this.innerHTML = '<div class="check">'+player+'</div>';
            x[ida][idb] = player;
            player = (player == 'O')? 'X' : 'O';
            checkWin(x, clickEvent);
        }
    };

    for(var i = 0; i < box.length; i++){
        box[i].innerHTML = '';
        box[i].addEventListener("click", clickEvent, false);
    }

    document.getElementById('resetBtn').onclick = function(){
        startGame();
    };
}
Enter fullscreen mode Exit fullscreen mode

This serves as the start of the game when the page is loaded as well as when the "reset board" button is clicked. For the sake of simplicity, in this game "O" will always be first, so we set that with

let player = 'O';
Enter fullscreen mode Exit fullscreen mode

We want to make sure the notification isn't showing anything until the game has ended so we set that element to blank

document.getElementById('notification').innerHTML = '';
Enter fullscreen mode Exit fullscreen mode

Then I put the board boxes into an array with each array representing the top, the middle and the bottom set of boxes respectively

var x = [['0','0','0'],['0','0','0'],['0','0','0']];
Enter fullscreen mode Exit fullscreen mode

Next I placed the box elements into a variable

let box = document.getElementsByClassName('box');
Enter fullscreen mode Exit fullscreen mode

Then I created a click event variable so I could set and unset the click action of the boxes

var clickEvent = function() {
  let ida = this.getAttribute('data-ida');
  let idb = this.getAttribute('data-idb');
  if(this.innerText == ''){
   this.innerHTML = '<div class="check">'+player+'</div>';
   x[ida][idb] = player;
   player = (player == 'O')? 'X' : 'O';
   checkWin(x, clickEvent);
  }
};
Enter fullscreen mode Exit fullscreen mode

Broken down: if you remember in the board, I set 2 different data attributes for each of the box elements: data-ida and data-idb. Earlier in the Javascript, I set a group of arrays which represented the board. These data attributes represent each position of the array so when we click on a box, we can change that specific position of the array and check to see if there is a win. So the first thing I did was set them to their own variables

let ida = this.getAttribute('data-ida');
let idb = this.getAttribute('data-idb');
Enter fullscreen mode Exit fullscreen mode

Then, if the clicked box element is empty

if(this.innerText == ''){
Enter fullscreen mode Exit fullscreen mode

we fill the box with either an 'O' or an 'X', depending on which player's turn it is

this.innerHTML = '<div class="check">'+player+'</div>';
Enter fullscreen mode Exit fullscreen mode

we then change the array we created earlier, using the position set out in the element's data attributes that were set in the variables at the start

x[ida][idb] = player;
Enter fullscreen mode Exit fullscreen mode

then we check to see which player is currently set and change it to the next player

player = (player == 'O')? 'X' : 'O';
Enter fullscreen mode Exit fullscreen mode

finally, we call on the checkWin function which we will create a little later

checkWin(x, clickEvent);
Enter fullscreen mode Exit fullscreen mode

Now all that's in place, we need to iterate through the box elements to set the click event for each one

for(var i = 0; i < box.length; i++){
    box[i].innerHTML = '';
    box[i].addEventListener("click", clickEvent, false);
}
Enter fullscreen mode Exit fullscreen mode

As you can see, because it serves as a fresh game, it's clearing out all of the boxes before setting the event listener. Now when you click on the boxes, they will be filled with either an 'O' or an 'X'. Should you click on 'reset board', they'll be emptied again. Well, actually it'd currently throw an error because the 'checkWin' function hasn't been written yet.

Finally in the startGame function, I added the click event for the reset button which basically calls upon the startGame function whenever it's clicked

document.getElementById('resetBtn').onclick = function(){
    startGame();
}
Enter fullscreen mode Exit fullscreen mode

And that's that for the startGame function. Next we move on to the checkWin function

function checkWin(x, c){
    var a = 0;
    var b = 0;
    var winningCombinations = [
        [x[0][0] + x[1][0] + x[2][0]],
        [x[0][1] + x[1][1] + x[2][1]],
        [x[0][2] + x[1][2] + x[2][2]],
        [x[0][0] + x[0][1] + x[0][2]],
        [x[1][0] + x[1][1] + x[1][2]],
        [x[2][0] + x[2][1] + x[2][2]],
        [x[0][0] + x[1][1] + x[2][2]],
        [x[0][2] + x[1][1] + x[2][0]]
    ];
    var win;

    for(var i = 0; i < 8; i++){
        if(winningCombinations[i].includes('XXX')){
            a++;
        }
        if(winningCombinations[i].includes('OOO')){
            b++;
        }
    }

    if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
        win = "next turn";
    }
    else{
        let sblbody = document.getElementsByClassName('sblbody');
        if(a == 0 && b == 0){
            win = "draw";
        }
        else if(a > 0 && b == 0){
            win = "X wins";
            sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
        }
        else if(a == 0 && b > 0){
            win = "O wins";
            sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
        }
        else if(a > 0 && b > 0){
            win = "error";
        }
        gameEnd(win, c);
    }
}
Enter fullscreen mode Exit fullscreen mode

It looks a lot, but it's quite simple really when it's broken down. First I set a couple of variables to represent each player, with a for 'X' and b for 'O'

var a = 0;
var b = 0;
Enter fullscreen mode Exit fullscreen mode

Then I had to work out the different winning combinations and map them to the array we created earlier in the startGame function. There are 8 possible winning combinations: 3 across, 3 down and 2 diagonal. If you imagine the arrays lined up on one another, it's easy to determine what the possible combinations would be and place them in their own variable

var winningCombinations = [
    [x[0][0] + x[1][0] + x[2][0]],
    [x[0][1] + x[1][1] + x[2][1]],
    [x[0][2] + x[1][2] + x[2][2]],
    [x[0][0] + x[0][1] + x[0][2]],
    [x[1][0] + x[1][1] + x[1][2]],
    [x[2][0] + x[2][1] + x[2][2]],
    [x[0][0] + x[1][1] + x[2][2]],
    [x[0][2] + x[1][1] + x[2][0]]
];
Enter fullscreen mode Exit fullscreen mode

Next I set an empty win variable, then iterated through the winning combinations to see whether 'X' or 'O' had a three in a row, if one or the other did, then I would add one to their respective a or b variable

var win;
for(var i = 0; i < 8; i++){
    if(winningCombinations[i].includes('XXX')){
        a++;
    }
    if(winningCombinations[i].includes('OOO')){
        b++;
    }
}
Enter fullscreen mode Exit fullscreen mode

Then, if neither has scored a win, we check to see if there are any moves left on the board by checking if any of the combined arrays contain a 0. If there are moves left, the game continues

if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
    win = "next turn";
}
Enter fullscreen mode Exit fullscreen mode

If there are no moves left on the board or a player has scored a win, we check through each of the possibilities

else{
    let sblbody = document.getElementsByClassName('sblbody');
    if(a == 0 && b == 0){
        win = "draw";
    }
    else if(a > 0 && b == 0){
        win = "X wins";
        sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
    }
    else if(a == 0 && b > 0){
        win = "O wins";
        sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
    }
    else if(a > 0 && b > 0){
        win = "error";
    }
    gameEnd(win, c);
}
Enter fullscreen mode Exit fullscreen mode

Broken down: first I set a variable for the score board's score elements

let sblbody = document.getElementsByClassName('sblbody');
Enter fullscreen mode Exit fullscreen mode

then I checked to see if 'X' and 'O' had no winning combinations, in which case the win variable would be set to draw

if(a == 0 && b == 0){
    win = "draw";
}
Enter fullscreen mode Exit fullscreen mode

next I checked if 'X' had a winning combination. If it did then the win variable would be set to "X wins" and the score element for 'X' would be increased by 1

else if(a > 0 && b == 0){
    win = "X wins";
    sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
}
Enter fullscreen mode Exit fullscreen mode

then I check if 'O' had a winning combination and repeated what I did for 'X'

else if(a == 0 && b > 0){
    win = "O wins";
    sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
}
Enter fullscreen mode Exit fullscreen mode

then I checked if both 'X' and 'O' had winning combinations, in which case an error would be thrown

else if(a > 0 && b > 0){
    win = "error";
}
Enter fullscreen mode Exit fullscreen mode

Finally I called upon the gameEnd function which we will write next

gameEnd(win, c);
Enter fullscreen mode Exit fullscreen mode

And that's all there is to the checkWin function. Let's move on to the final function, gameEnd

function gameEnd(x, c){
    document.getElementById('notification').innerText = x;
    let box = document.getElementsByClassName('box');
    for(var i = 0; i < box.length; i++){
        box[i].removeEventListener("click", c, false);
    }
}
Enter fullscreen mode Exit fullscreen mode

A pretty basic function. First we set the notification element to display the win/draw/error status

document.getElementById('notification').innerText = x;
Enter fullscreen mode Exit fullscreen mode

Then we iterate through the box elements and remove the click events to prevent the game from continuing

let box = document.getElementsByClassName('box');
for(var i = 0; i < box.length; i++){
    box[i].removeEventListener("click", c, false);
}
Enter fullscreen mode Exit fullscreen mode

That's all there is to the gameEnd function. Now you can start playing. You're going to notice, though, that the 'X' and 'O' aren't of a proper size, so there's one more piece of style we have to add to the game

.check{
    font-size: 130px;
    text-align: center;
}
Enter fullscreen mode Exit fullscreen mode

The complete code:

HTML

<div id="container">
    <div id="board">
        <div class="box" data-ida="0" data-idb="0"></div>
        <div class="box" data-ida="0" data-idb="1"></div>
        <div class="box" data-ida="0" data-idb="2"></div>
        <div></div>
        <div class="box" data-ida="1" data-idb="0"></div>
        <div class="box" data-ida="1" data-idb="1"></div>
        <div class="box" data-ida="1" data-idb="2"></div>
        <div></div>
        <div class="box" data-ida="2" data-idb="0"></div>
        <div class="box" data-ida="2" data-idb="1"></div>
        <div class="box" data-ida="2" data-idb="2"></div>
        <div id="resetBtn">Reset board</div>
    </div>
    <div id="score_board">
        <div id="sbhead">SCORE</div>
        <div class="score_block">
         <div class="sblhead">O</div>
         <div class="sblbody" data-id="o">0</div>
        </div>
        <div class="score_block">
         <div class="sblhead">X</div>
         <div class="sblbody" data-id="x">0</div>
        </div>
    </div>

    <div id="notification"></div>
</div>
Enter fullscreen mode Exit fullscreen mode

CSS

html, body{
    font-family: arial;
}

#container{
    display: flex;
    justify-content: center;
    align-items: center;
}

#board{
    text-align:center;
    width: 466px;
    position: absolute;
    left: calc(50% - 338px);
    top: 35px;
}

.box{
    border: 1px solid;
    width: 150px;
    height: 150px;
    display: inline-block;
    vertical-align: top;
    cursor: pointer;
}

.check{
    font-size: 130px;
    text-align: center;
}

#resetBtn{
    margin-top: 10px;
    padding: 15px;
    background-color: green;
    width: 100px;
    position: absolute;
    left: calc(50% - 65px);
    color: #fff;
    font-weight: bold;
    cursor: pointer;
    border-radius: 15px;
    box-shadow: 3px 3px lightgrey;
    transition: .3s;
}

#resetBtn:active{
    margin-top: 12px;
    left: calc(50% - 63px);
    box-shadow: 1px 1px lightgrey;
}

#notification{
    position: absolute;
    text-align: center;
    top: 560px;
    font-size: 50px;
    margin-right: 209px;
}

#score_board{
    border: 1px solid;
    width: 200px;
    height: 453px;
    top: 35px;
    margin-left: 467px;
    position: absolute;
    text-align: center;
    font-weight: bold;
}

#sbhead{
    padding: 7px 0;
    font-size: 20px;
    border-bottom: 1px solid;
}

.score_block{
    border-bottom: 1px solid;
    height: calc(50% - 20px);
}

.sblhead{
    padding: 5px;
    border: 1px solid;
    font-size: 32px;
}

.sblbody{
    font-size: 114px;
}
Enter fullscreen mode Exit fullscreen mode

Javascript

startGame();
function startGame(){
    let player = 'O';
    document.getElementById('notification').innerHTML = '';
    var x = [['0','0','0'],['0','0','0'],['0','0','0']];
    let box = document.getElementsByClassName('box');

    var clickEvent = function() {
        let ida = this.getAttribute('data-ida');
        let idb = this.getAttribute('data-idb');
        if(this.innerText == ''){
            this.innerHTML = '<div class="check">'+player+'</div>';
            x[ida][idb] = player;
            player = (player == 'O')? 'X' : 'O';
            checkWin(x, clickEvent);
        }
    };

    for(var i = 0; i < box.length; i++){
        box[i].innerHTML = '';
        box[i].addEventListener("click", clickEvent, false);
    }

    document.getElementById('resetBtn').onclick = function(){
        startGame();
    };
}

function checkWin(x, c){
    var a = 0;
    var b = 0;
    var winningCombinations = [
        [x[0][0] + x[1][0] + x[2][0]],
        [x[0][1] + x[1][1] + x[2][1]],
        [x[0][2] + x[1][2] + x[2][2]],
        [x[0][0] + x[0][1] + x[0][2]],
        [x[1][0] + x[1][1] + x[1][2]],
        [x[2][0] + x[2][1] + x[2][2]],
        [x[0][0] + x[1][1] + x[2][2]],
        [x[0][2] + x[1][1] + x[2][0]]
    ];
    var win;

    for(var i = 0; i < 8; i++){
        if(winningCombinations[i].includes('XXX')){
            a++;
        }
        if(winningCombinations[i].includes('OOO')){
            b++;
        }
    }

    if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
        win = "next turn";
    }
    else{
        let sblbody = document.getElementsByClassName('sblbody');
        if(a == 0 && b == 0){
            win = "draw";
        }
        else if(a > 0 && b == 0){
            win = "X wins";
            sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
        }
        else if(a == 0 && b > 0){
            win = "O wins";
            sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
        }
        else if(a > 0 && b > 0){
            win = "error";
        }
        gameEnd(win, c);
    }
}

function gameEnd(x, c){
    document.getElementById('notification').innerText = x;
    let box = document.getElementsByClassName('box');

    for(var i = 0; i < box.length; i++){
        box[i].removeEventListener("click", c, false);
    }
}
Enter fullscreen mode Exit fullscreen mode

And that's it. A basic Tic Tac Toe game in Javascript.

Discussion (6)

Collapse
lukeshiru profile image
LUKESHIRU

A few tips:

  • Avoid using innerHTML, and instead use textContent if you want to set the text inside an element, or create a new element with document.createElement and then append it to the parent. Using strings for that is just asking for trouble.
  • Avoid using == or != and instead use === and !==.
  • You don't need that third argument in addEventListener.
  • You should be consistent, in one line you use addEventListener and in the other onClick. Try using always addEventListener.
  • Instead of using getElementById every time the function runs, you could just call it once and save it in a constant outside the function.
  • Remember to always pass the radix to parseInt (the second argument set to 10 for decimal values).
  • Instead of using for you could use forEach:
// Instead of this:
let box = document.getElementsByClassName("box");

for (var i = 0; i < box.length; i++) {
    box[i].removeEventListener("click", c, false);
}

// Just do this:

[...document.querySelectorAll(".box")].forEach(box =>
    box.removeEventListener("click", c),
);
Enter fullscreen mode Exit fullscreen mode
  • Avoid single letter variables like c and use something more significant, like callback.

That's it! Cheers!

Collapse
ozboware profile image
ozboware Author

Doesn't textContent just render text? I've never had any trouble using innerHTML compared to createElement.

If I were to constantly use the strict equality operator, I'd have to constantly convert numbers as strings back to numbers again, I don't see using only the strict equality operators as being a good use of time

In one I used onclick because I didn't need to remove the click event, in fact onclick is the one I generally use if I'm not using jquery and bind.

There was no need to pass the radix in parseInt, it worked beautifully without it.

Why use forEach instead? for worked out well enough.

I used the single letter variable in the functions to save having to write out the more significant name (which I used for the original variable) multiple times.

The only thing I'll give you there is calling on the elements outside the function, which I would have noticed if I didn't just do the project for the sake of showing myself I can.

There's a lot of it has to be done one certain way with your methodology, which may work out great for you, my way works out just as great for me

Collapse
lukeshiru profile image
LUKESHIRU

Doesn't textContent just render text? I've never had any trouble using innerHTML compared to createElement.

Yes, textContent only lets you change the text content of an element, which is way safer than using innerHTML. If you're getting that code from an unreliable source (user input, external API, etc) then your code is unsafe, and if you're writing it yourself, you could have a typo and break it as well.

Instead of doing something like this:

this.innerHTML = `<div class="check">${player}</div>`;
Enter fullscreen mode Exit fullscreen mode

You could have that div already there and just do div.textContent = player. And if you actually need to create that from scratch (I believe you don't for this tic-tac-toe), then you could:

const check = document.createElement("div");
check.classList.add("check");
check.textContent = player;
Enter fullscreen mode Exit fullscreen mode

Or if you want the "short version":

const check = Object.assign(document.createElement("div"), {
    className: "check",
    textContent: player
});
Enter fullscreen mode Exit fullscreen mode

And then append that check to your element. One thing I noticed doing that example is that you used div quite a lot, and that's also something you should avoid. Instead, you should use more accessible elements, such as button.

If I were to constantly use the strict equality operator, I'd have to constantly convert numbers as strings back to numbers again, I don't see using only the strict equality operators as being a good use of time

I'm noticing a pattern here. Unsafe code is "shorter", yes, but the time you "save" is later spent fixing bugs in your code. Strict comparison ensures that you're actually comparing values that have the same type, which is way safer than assuming that the type casting will work all the time.

In one I used onclick because I didn't need to remove the click event, in fact onclick is the one I generally use if I'm not using jquery and bind.

Yup, but keeping your code consistent is important for maintenance. I mean is fine if you want to code something that you'll just discard, but not if is something you plan on improve in the future.

There was no need to pass the radix in parseInt, it worked beautifully without it.

Again, unsafe.

Why use forEach instead? for worked out well enough.

Again, safer to use something that doesn't rely on your possible typos. forEach will always loop over all the elements, and don't require you to have i. Heck, even if you want to stay with for, it would be better to use for..of.

I used the single letter variable in the functions to save having to write out the more significant name (which I used for the original variable) multiple times.

Why not keep using significant variable names everywhere? Is not like the code has to be readable in some places and not in others. It needs to be always readable.

There's a lot of it has to be done one certain way with your methodology, which may work out great for you, my way works out just as great for me

The thing is that the tips I wrote aren't for "my way", is more like the "safe/maintainable way" of doing things. It will save you some future headaches. Just because you aren't planning on getting back to this code in the future, that doesn't mean you should just make it "unsafe". "It works" generally is not enough.

Cheers!

Thread Thread
ozboware profile image
ozboware Author • Edited on

Yes, textContent only lets you change the text content of an element, which is way safer than using innerHTML

You could use innerText for that. I also didn't use it just to insert text, I added an element with it as I didn't want the element always there. I don't see any safety issues or examples given for it being unsafe the way I used it.

If you're getting that code from an unreliable source (user input, external API, etc) then your code is unsafe

It isn't coming from user input.

One thing I noticed doing that example is that you used div quite a lot, and that's also something you should avoid. Instead, you should use more accessible elements, such as button.

Why? There's nothing wrong with divs, many of the top sites are flooded with them. I didn't want to use a button, or a submit input field. I would only have had to style them anyway, it doesn't give me any more use than a div in this instance.

I'm noticing a pattern here. Unsafe code is "shorter", yes, but the time you "save" is later spent fixing bugs in your code. Strict comparison ensures that you're actually comparing values that have the same type, which is way safer than assuming that the type casting will work all the time.

I'm noticing a pattern, too. A lot of comments from devs on forums are from people touting safety, but never really showing why something is unsafe. Sure, if you want to check that both values are of the same type then use the strict operator, otherwise there's no need. What's somebody going to do here, put in some text on the score board and mess up their own experience without doing any damage internally?

Yup, but keeping your code consistent is important for maintenance. I mean is fine if you want to code something that you'll just discard, but not if is something you plan on improve in the future.

Depends. I can go back to this in two years and not get confused by onclick, addAttribute or clickEvent

Again, unsafe.

Great example. Seeing the pattern here.

Again, safer to use something that doesn't rely on your possible typos. forEach will always loop over all the elements, and don't require you to have i. Heck, even if you want to stay with for, it would be better to use for..of.

safer to use something that doesn't rely on your possible typos.

easier for those who consistently have typos, perhaps, not really safer. At the worst, it'd throw an error that needs to be fixed as you go along, but it doesn't make it "unsafe"

Heck, even if you want to stay with for, it would be better to use for..of

Why?

Why not keep using significant variable names everywhere? Is not like the code has to be readable in some places and not in others. It needs to be always readable.

Because it's not necessary and is readable

The thing is that the tips I wrote aren't for "my way", is more like the "safe/maintainable way" of doing things. It will save you some future headaches. Just because you aren't planning on getting back to this code in the future, that doesn't mean you should just make it "unsafe". "It works" generally is not enough.

Except it isn't "unsafe", it's completely maintainable and it works.

Thread Thread
lukeshiru profile image
LUKESHIRU • Edited on

You could use innerText for that.

textContent is safer than innerText.

It isn't coming from user input.

Yup, is still coming from you, who could miss a < or some other thing and endup with invalid HTML. That will not happen if you actually create elements in memory (besides you can reuse them so you don't have to add the events every time).

Why? There's nothing wrong with divs, many of the top sites are flooded with them.

One word: Accessibility.

I'm noticing a pattern, too. A lot of comments from devs on forums are from people touting safety...

Haven't you considered that maybe is because safety is kinda important?

At the worst, it'd throw an error that needs to be fixed as you go along, but it doesn't make it "unsafe"

That's exactly what makes it unsafe. Unsafe is not just "hackers can get your password", is just means that the app can "crash" :/

Because it's not necessary and is readable

I get it now, so you never worked in a team, right?

Except it isn't "unsafe", it's completely maintainable and it works.

I think the problem here is that you don't actually get what "unsafe" means. Still, the issue is also that you don't actually thought your code as something to be shared or used by others, which is ok if you don't plan in working with a team, but it will bite you later on.

Still, I'll leave it here. I wanted to offer some tips and I explained those, and maybe is just me, but your answers seem to be getting more and more heated, so I'll just stop before it gets worse. Maybe for next time you should consider that when you post an article with code, the comments will not be only prasing what you did, they could also be suggestions to improve it, so you might want to be prepared for more feedback from other folks in the future.

Thread Thread
ozboware profile image
ozboware Author

textContent is safer than innerText

The output from the example you gave are identical between the two.

Yup, is still coming from you, who could miss a < or some other thing and endup with invalid HTML.

Who does that and doesn't notice?

One word: Accessibility.

It's accessible to everyone it should be accessible to given the functions. It's not a website, it's a visual hands on application

Haven't you considered that maybe is because safety is kinda important?

Sure, if it's really about safety. You seem to just be throwing the word about at will as an explanation why things are best done your way.

That's exactly what makes it unsafe. Unsafe is not just "hackers can get your password", is just means that the app can "crash"

That's not unsafe. It crashes, you realise your error, you fix it before releasing it. That's why you test everything you do as you go along.

I think the problem here is that you don't actually get what "unsafe" means. Still, the issue is also that you don't actually thought your code as something to be shared or used by others, which is ok if you don't plan in working with a team, but it will bite you later on.

The examples you've given me won't affect me later on. I know what unsafe means, I also know you're trying to broaden the term. At best all you've really got is: something may be more prone to error than something else, if a mistake is made, which can be true of anything. I've also shared the code and stated it's not a definitive "how to" on the subject. If somebody wants to use portions of the code and thinks they could do other parts better or differently, that would be up to them.

Still, I'll leave it here. I wanted to offer some tips and I explained those, and maybe is just me, but your answers seem to be getting more and more heated, so I'll just stop before it gets worse.

They're not getting heated. I'm disagreeing with you. You're telling us we should do things one way and coming up with online reasons for an offline project and not even good reason, just a generalisation of why one tag should always be used over another.

Maybe for next time you should consider that when you post an article with code, the comments will not be only prasing what you did, they could also be suggestions to improve it

You gave one useful suggestion of an improvement and saved the document.getElementById written out one more time on the sheet. The rest was just telling us why certain commands shouldn't be used because they're unsafe while not giving any examples of why they're unsafe in what you've just read

so you might want to be prepared for more feedback from other folks in the future.

I'm prepared, that's why I'm not ignoring you