JavaScript vs jQuery for DOM Manipulation

jQuery has been the savior for so many new and coming Web Developers, including myself. If you wanted to learn Web Development back in the day, learning jQuery was an absolute given. This was mainly because jQuery took much of the cross-browser compatibility issues out and enabled developers to write code without having to worry about whether the features that they are implementing will work on all browsers.

But with improvements in browser standards and most of jQuery’s API’s integrated into JavaScript, jQuery has become a little redundant. Moreover, with native browser API’s it is so much easier to debug code, and being native, most of these API’s offer better performance than jQuery’s API’s. Besides, you will have one less library to add to your script imports. If you’re still not sold on parting with jQuery maybe this answer will help.

So, if you’re considering a move away from jQuery, I have compiled a list of common jQuery methods and API’s that people use, with their Vanilla JS alternatives (Vanilla JS is a fancy name for plain JavaScript code without the use of any libraries). Let’s dive in!

Querying the DOM

The bread and butter of jQuery is it’s amazing ability to query DOM elements. This is demonstrated below:


However, you can achieve the same thing with JavaScript using it document.querySelector() and document.querySelectorAll() methods. Below is their implementation.


This method always returns the first element that matches the selector. If you’re expecting to query multiple elements, you will have to use the querySelectorAll() method.

document.querySelectorAll('img').forEach((_el, index) => {
      //do stuff with _el here

querySelectoryAll() will return an array of matched elements, hence the use of forEach to iterate over each element.


.html() method of jQuery is used to get or set the HTML content of  a node/element.

To do the same in JavaScript you can use the .innerHtml property of any HTML element.

Let’s say you need to get the HTML content of a div element with class main-div. You could do this in JavaScript like this.

var htmlContent = document.querySelector('.main-div').innerHtml;

Similarly to set the HTML content of the same div, you could do something like this:

document.querySelector('.main-div').innerHtml = '<p>Hello World</p>';

Remember, if you want to change the Html of multiple elements with the same selector, you can use the querySelectorAll() method like this:

document.querySelectorAll('.main-div').forEach((el, index) => {
    _el.innerHtml = '<p>Hello World</p>';

Notice that I pass in the index to the anonymous function here. You don’t really need it in this case, but it’s a good practice to keep the index of each element of the array handy. You never know when you may need it.


.val() method of jQuery is used to get or set the value of an input element.

To get or set the value of an input element in JavaScript, you can use the value property of that element. 

To set the value of an input element with class input-box to ‘new’

document.querySelector('.input-box').value = 'new';

To get the value of the same element

var inputValue = document.querySelector('.input-box').value;


jQuery offers .text() method to get all the content of an element without the markup. e.g., using jQuery to get the text of <p>Hi</p> element will return the string ‘Hi’. This can be very useful when you’re trying to get the content actually visible in the web-page.

That being said, every HTML element has a property “innerText” that essentially holds the same value. 

let textValue = document.querySelector('.main-div').innerText;

Styling the DOM

One of the most exciting features of jQuery is the ability to modify the styling of our DOM. This is highly useful when you’re trying to have a visual feedback to a user interaction.

Modifying styles can be done by one of two ways:

  1. Adding/Removing Classes:
    To add or remove a class you will most definitely use .addClass() or .removeClass() methods that ship with jQuery.
    To do the same thing in JavaScript, you can use the classList property of an element. This can be done as shown below.
    To add the class testClass to an element with Id testElement


    To remove the class testClass from the element with Id testElement


  2. Adding/Modifying CSS:
    To directly change the CSS of an element we use the .css() method provided by jQuery. Once again, this is fairly simple to do in JavaScript.
    To set a style for an element, we simply set the value of that style as exposed by the style property of that element.

    document.querySelector('#testElement').style.color = '#ffffff';

    This will set the color of the element with Id testElement to white.
    Note: Since the styles of element are exposed as JavaScript object properties, you won’t be able to use properties such as background-color directly as there is a “-” in the property name. In these situations, you should simply use the camelCased version of these properties. e.g.,

    document.querySelector('#testElement').style.backgroundColor = '#000000';

    This will set the background-color of element with Id testElement to black.


Setting and retrieving values of the attributes of HTML elements is once again very useful when developing a web application. You may need to get the value of a data- attribute that is storing important data while working with your application. jQuery provides this ability by exposing .attr() method. You can use it with one parameter to get the value of an attribute or use an overload with two parameters to set the value of an HTML attribute. 

In JavaScript every attribute of an HTML element is exposed as a property of that element. This means that to set the value of the src attribute of an img element, you can simply do the following:

document.querySelector('img').src = '/images/image.png';

It’s that simple. For attributes such as data- you can use another approach,

document.querySelector('img').setAttribute('data-title', 'test');

To get the value of the same element,


The getAttribute() and setAttribute() bear a striking resemblance to jQuery’s attr() method. So, getting used to these shouldn’t be that difficult.

.show() / .hide()

Once again, two of the most widely used jQuery methods are the .show() and .hide() methods. These methods do exactly what they say they do, hide or show HTML elements. In their essence, these methods are simply modifying the display style property of elements. So, we can use simple style modification to achieve the same thing.

To hide an element:

document.querySelector('.test-element').style.display = 'none';

To show a hidden element

document.querySelector('.test-element').style.display = 'block';


One of my favorite and most used jQuery functionality is sending HTTP requests using AJAX. However, we have to understand that jQuery.ajax() is a wrapper around existing JavaScript functionality. There is no doubt that jQuery makes using AJAX a breeze, but it’s not why we’re here, is it?

To send a POST request to the URL ‘home/sendmessage’ with the data {id: 23, name: ‘faisal’}, we’d do the following:

let request = new XMLHttpRequest();

// Set a event handler when the status of the request changes
request.onreadystatechange = function(response) {
    if (request.readystate == 4 && request.status == 200) {
        // do stuff with the response object

// Open a Post request to the url home/sendmessage asynchronously.'POST', 'home/sendmessage', true);

// Set the request header/s
request..setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

//Send parameterized data as part of the Post request

That’s it. You post request will be sent to the server.

If you’re a jQuery developer, you will know that these aren’t all the methods and features that jQuery ships with. There are definitely more, but I’ve simply tried to address the most common ones just to give you an idea that life without jQuery in the browser is possible.

I hope that it’s enough to push you towards using more and more JavaScript.

How to Write an Unbeatable Tic-Tac-Toe in JavaScript

Writing small puzzle games is a nice way of improving your programming skills as those games tend to present you with challenge after challenge and force you to really put tremendous thought into writing your code. 

One such game is the classic Tic-Tac-Toe.

In this little walk-through (read long and descriptive) about writing an unbeatable tic-tac-toe, I will be using Vanilla JavaScript for logic and HTML/CSS for UI. I won’t be concentrating much on UI part of it, because, duh! 

Another little disclaimer – we will be concentrating on logic primarily and code will come second to that. Again, if you want to skip the “walk-through” and jump right to the code, I have a public repository on GitHub at I also have a live version of the game available at

So, let’s dive in.

First of all you need to create the UI. Just use simple HTML to create 9 input boxes, or divs even to hold your X’s and O’s. The id’s of these input boxes are important as they will be our reference to the X’s and O’s. To keep it consistent with the positioning of our boxes, I have given the input boxes the id’s “0-0” to “2-2”. To visualize: 


This will give us an easy to use 3×3 matrix. You can design your UI whatever way you want. It should simply employ this form of queryable HTML elements. 

Here’s a screenshot of my UI. Simple boxes with a little gloss of CSS.

Now, the logic! First of all, whenever a user, or a participant in this case, clicks or taps an empty box, the value of that box should change to either an X or an O, depending upon whose turn it is.

Implementing that is simple. I use a global variable to hold the ‘currentPlayer‘. This variable has a value of either X or O. Then I simply write a method to toggle current player, whenever a box is tapped. I call the method changePlayer.

All this is started by tapping a box. So, we need a function to get us started. We write a function that marks the box with X or O and changes player. We call the function onCheckBox. So far, the function checks a box and and then changes player.

Now, while the boxes are being tapped, we need to keep track of the checked boxes. Using an array should help us get this job done. Simply holding the id’s of all the boxes is useful, but it’s not enough. We will definitely need to hold the id’s (co-ordinates in this case) as well as the player who checked the box. So, our checkedBoxes array will be an array of objects of this form:

[{box: ‘0-1’, player: ‘X’}, {box: ‘2-1’, player: ‘O’}, …]

We now have two global variables – one to hold currentPlayer and other to hold checkedBoxes.

Another thing that I like to keep track of is the Turn Count. I can get that from the chekedBoxes array, but this way it’s simpler and very useful if I’m manipulating the checkedBoxes array.

Since we will be writing a Player vs CPU mode only (github repo contains both 1p and 2p modes), these are all the global variables that we are going to need – checkedBoxescurrentPlayer and turnCount.

NOTE: It’s always good practice to keep your global variables at a minimum to avoid dependencies and unexpected behavior within functions.

I believe our onCheckBox method is now ready. This is what it looks like

function onCheckBox(element) {
   checkedBoxes.push({ box:, player: currentPlayer });
   var   gameStatus =checkWinner();   //Will come to this method in a bit
}function checkElement(element){
     element.value = currentPlayer;     
     element.disabled ="disabled";
}function switchPlayer() {
    currentPlayer = currentPlayer =="X"?"O":"X";
    document.querySelector('.current-player').textContent = currentPlayer;

We are passing the clicked Html element as one of the parameters to the method to access it’s id for coordinates. Everytime we check a box we need to check whether there is a winner or whether the game is drawn.

This brings us to our the checkWinner method. This method simply checks whether the X’s or O’s are in a single line (rules of tic-tac-toe) to determine whether there is a winner. It’s not very difficult to build that logic. We query the checkedBoxes array and find all the coordinates with player X. If the coordinates follow the pattern (a-0, a-1, a-2) or (0-a, 1-a, 2-a) where a = 0 or 1 or 2, then we have a horizontal or vertical line checked. Well, diagonal lines are a little tricky. For right to left diagonal, we use (a-a, b-b, c-c) and for left to right diagonal we use (0-2, 1-1, 2-0). If we find any of these patterns in the checkedBox matrix then the ‘player’ having the pattern is the winner. If none of these patterns match, then we simply check whether one of the two players has checked 5 boxes to determine that it’s a draw. 

function checkWinner(isCheckOnly=false) {
if (currentPlayer =="X") {
//Get all the boxes marked by Player X
   var  xs = checkedBoxes.filter(item=> item.player =="X").map(value=> {
     return { x: Number("-")[0]), y: Number("-")[1]) }
else if (currentPlayer =="O") {
//Get all the boxes marked by Player O
   var  os = checkedBoxes.filter(item=> item.player =="O").map(value=> {
     return { x: Number("-")[0]), y: Number("-")[1]) }
}function calculateScore(positions) {
//Check right diagonalif (positions.filter(i=> { return i.x == i.y }).length ==3) {
              return'game over';
}//Check Left diagonal
if (positions.filter(i=> { return (i.x ==0&& i.y ==2) 
                                    || (i.x ==1&& i.y ==1)
                                    || (i.x ==2&& i.y ==0) }).length ==3) {
                return'game over';
//check horizontal match
for (var i =0; i <3; i++) {
  var  consecutiveHorizontal = positions.filter(p=> p.x == i);
  if (consecutiveHorizontal.length ==3) {
       return'game over';
//check vertical match
  var  consecutiveVertical = positions.filter(p=> p.y == i);
  if (consecutiveVertical.length ==3) {
        return'game over';
//check draw
if (positions.length ==5) {
  return'game drawn';
//if none of the conditions match 'game on'
return'game on';

Here I’m delegating the responsibility of finding result to calculateScore method. These two methods are essentially translating what we said above to JavaScript code. Nothing fancy here.

Let’s try something now. Shall we? I believe our game is now ready to handle all the tic-tac-toe logic. What remains is making it unbeatable. To make the game unbeatable, I want to set some ground rules to avoid making this post excruciatingly long (you’re kidding, right). The CPU will always play second and will be primarily focused on not letting the opponent win. So, our onCheckBoxmethod will see the addition of the method computerPlays for CPU’s turn. 

function onCheckBox(element) {
checkedBoxes.push({ box:, player:
    currentPlayer });
     gameStatus = checkWinner();
  if (turnCount %2 == 1&&
    gameStatus == 'game on'){

As you can see the computerPlays method will only be called if the turnCount is odd, ie, it’s 2nd, 4th, 6th or 8th turn, and if gameStatus returned by the checkWinner method is ‘game on’ as there is no point in continuing the game, if the game is already over. To understand how to counter a human move, I came up with 5 cases. These five cases in the order of their priority are: 

  1. First Move
  2. Finishing Move that allows CPU to win the game.
  3. Saving Move that allows CPU to save the game.
  4. Predicting a move that will trap the CPU and avoiding it.
  5. And if none of these fit, a Random move.

Here’s a sneak preview of our computerPlays method.

function computerPlays() {
  var  nextBoxCoords;
  if(turnCount ==1){
nextBoxCoords =computeFirstMove();
  if (!nextBoxCoords){
nextBoxCoords =computeFinishingMove();
}  if (!nextBoxCoords) {
nextBoxCoords =computeSavingMove();
  if (!nextBoxCoords)
nextBoxCoords =predictTrappingMove();
  if (!nextBoxCoords) {
nextBoxCoords =computeRandomMove();
  var  nextBox = document.querySelector(`[id='${nextBoxCoords}']`);

1. First Move

Computing first move is out of experience, although, I could have written an algorithm for it. But to avoid complexity, I just went with past experience. If it’s CPU’s first move, we need to find where the opponent played. If it’s center box, we return a corner box. Similarly, if he played a corner box, we return an edge box adjacent to it and finally if he played an edge box, we return the center box. By the way, 1-1 is the center box, 0-1, 1-0, 1-2, 2-1 are the edge boxes and, you guessed it, 0-0, 0-2, 2-0, 2-2 are the corner boxes.

Here it is,

function computeFirstMove(){
  var  playedMove =>[0];
  var  edgeMoves = ['0-1', '1-0', '1-2', '2-1'];
  var  cornerMoves = ['0-0', '0-2', '2-0', '2-2'];
  var  centerMove = ['1-1'];
  if(edgeMoves.find(m=> m == playedMove))
  else if(cornerMoves.find(m=> m == playedMove))
  else if(centerMove.find(m=> m == playedMove))
    return cornerMoves[Math.floor(Math.random()*cornerMoves.length)];
}function edgeMoveResponse(playedMove){
  if(playedMove =='1-2') 
  else if (playedMove =="0-1") 
  else if (playedMove =="1-0") 
  else if(playedMove =='2-1') 

To avoid cramming up the computeFirstMove method, I moved out the edgeMoveResponse method. Now, that we got the first move sorted, let’s move to the next check – computeFinishingMove

2. Finishing Move that allows CPU to win the game.

If the opponent is there for the taking we would prioritize taking him. Let’s say we have the following situation: 

Since, it’s CPU’s turn (O) we would definitely want to check the lower-right box. That’s where our computeFinishingMove method comes in. It simply iterates over all the remaining boxes and checks them one by one. It then calls the checkWinner method on each iteration and if there’s a winner, just returns the box of that iteration as the ‘nextBoxCoords‘ to the computerPlays method. Here’s the code: 

function computeFinishingMove() {
  var  remainingMoves =getRemainingMoves();
  var  finishingMoveCoords;
  for (var move of remainingMoves) {
    checkedBoxes.push({ box: move, player: currentPlayer });
    var  nextBox = document.querySelector(`[id='${move}']`)
    if (checkWinner() =='game over') {
finishingMoveCoords = move;
      onUncheckBox(nextBox, true);
    onUncheckBox(nextBox, true);
    console.log('Playing Finishing Move')
    return finishingMoveCoords;
}function getRemainingMoves() {
  var  allMoves = ['0-0', '0-1', '0-2',
                  '1-0', '1-1', '1-2',
                  '2-0', '2-1', '2-2',]
  var  playedMoves =>;
  return allMoves.filter(m=>!playedMoves.find(move=> move == m));
function onUncheckBox(elementisImplicit=false) {
checkedBoxes = checkedBoxes.filter(b=> !=;
  if (!isImplicit) {
element.value ='';

While checking a box to check for winner, we have to make sure that we uncheck that box after the iteration completes to avoid bad data in checkedBoxes array. For that matter, we employ the onUncheckBox method above. If there is no ‘finishing move’ available, we check for the next condition – computeSavingMove. 

3. Saving Move that allows CPU to save the game.

Consider the following scenario: 

Here it’s CPU’s (O) turn to play. Clearly playing anywhere other than top middle box will cause the game to end in the next turn. So, we have to definitely check that box. To do this, we write a method similar to previous method where we this time iterate over all the remaining boxes and during each iteration fill them with opponent’s mark and then call the checkWinner method. If it returns ‘game over’ in any of the iterations then that is our desired box. Here’s what this translates to in JavaScript. 

function computeSavingMove() {  var  remainingMoves =getRemainingMoves();  switchPlayer();  var  savingMoveCoords;  for (var  move of remainingMoves) {          checkedBoxes.push({ box: move, player: currentPlayer });         var nextBox = document.querySelector(`[id='${move}']`)         if (checkWinner() =='game over') {                         savingMoveCoords = move;                         onUncheckBox(nextBox, true);                         break;}    onUncheckBox(nextBox, true);}    switchPlayer();   if(savingMoveCoords){      console.log('Playing Saving Move')      return savingMoveCoords;}}

You might argue that this is a clear violation of the DRY (Don’t Repeat Yourself) principle. But, sometimes, to keep the code readable and avoid complexities, you may want to take a rain check on the DRY. To make sure that I’m iterating with opponent’s move and note CPU’s, I’m calling the switchPlayer method before iterating over remainingMoves. If none of the remaining moves is a saving move we then turn our attention to the trickiest condition – predictTrappingMove

4. Predicting a move that will trap the CPU and avoiding it.

A ‘Trapping Move’, as I like to call it is when the opponent checks a box that leaves you with two Finishing boxes to fill, ultimately leading you to lose the game. Here’s a screenshot to show exactly that: 

In the image, it’s Player O’s turn and he is in a lose-lose situation. If he check tile 0-0, Player’s X will check 2-2 to win the game and vice versa. So, the only way out of this situation is to avoid it. That’s where our next method, predictTrappingMove comes in.

This method isn’t complicated in terms of execution but the planning needs to be spot on. So, what we do is, every time the opponent plays a move, we check for the possibility of a trapping move, the next time he plays. To achieve this, we will have to one by one check every box with our move. On each turn, we will then have to check each remaining box with the opponents move. Then, we check whether more than one winning move was created as a result of that. I know, I know, what the hell, right? Okay let’s break it down. Suppose we are in this situation: 

The opponent is playing for O’s. If I, for instance, play 1-0, the opponent can easily play 0-2 and trap me like this. 

Since, there was no finishing or saving move available, I wasn’t able to detect this. To detect this, I will have to check every single box till I find one that’s safe. I can accomplish that by first checking box. Then I will play opponent’s turn on each of the remaining boxes.

This way, I can determine whether any of his moves will result in a trapping move. Any of my turns that I don’t find a trapping move anywhere, will be my next move. For some turns, it won’t be possible for the opponent to play all the available boxes, eg, a turn where he has to play a saving move. In those cases, I won’t be iterating over all his possible moves, but just the saving ones. I’ll be making the assumption that the opponent will try to save himself if I play a move that forces him to do so. Okay, this is how it all translates to JavaScript. 

function predictTrappingMove() {
  var  checkedBoxesBackup = checkedBoxes.slice();
  var  remainingMoves =getRemainingMoves();
  var  nextMove;
  var  moveFound;
  for(var move of remainingMoves){
checkedBoxes.push({box: move, player: currentPlayer})
//Check if the opponent needs to play a saving move
    var savingMove =  computeSavingMove();
checkedBoxes.push({box: savingMove, player: currentPlayer});
      if(checkTrap() =='no trap'){
nextMove = move;
//If no saving move is required, check each position  
     for(var opponentMove ofgetRemainingMoves()){
       moveFound =true;
       checkedBoxes.push({box: opponentMove, player: currentPlayer});
       if(checkTrap() =='trapped'){
         moveFound =false;
nextMove = move;
}checkedBoxes = checkedBoxesBackup;
  return nextMove;
function checkTrap(){
  var  boxes =getRemainingMoves();
  var  winningMoveCount =0;
  for(var  freeMove of boxes){
checkedBoxes.push({box: freeMove, player: currentPlayer});
    var result = checkWinner(true);
    if(result =='game over')
  if(winningMoveCount >1){
    return 'trapped';
    return 'no trap';

Here, predictTrappingMove is first checking a box on my behalf. Then, it’s checking whether the opponent needs to play a saving move to counter that. If yes, it’s calling the checkTrap method to see whether more than one saving conditions were created on that opponent move.

If not, that is the move that will be returned and played. If that’s not the case, however, and the opponent’s saving move created a trap for us, we need to move forward and check the remaining boxes for ourselves, and then in each turn check the remaining boxes on the opponent’s behalf and call the checkTrap method each time. If any of the boxes creates a ‘trapping move’, we avoid that move and keep iterating over remaining moves, till we find a safe move to play.

It may be a little confusing still, but if you go over the code a few times, it should all be clear. 

5. Random move

I wrote the computeRandomMove in the first versions of the game to calculate a move if all checks don’t return a move. However, it’s not relevant now as checkTrappingMove will always return a move, irrespective of everything. Here’s the code still if you need it. 

function computeRandomMove() {  var  remainingMoves =getRemainingMoves();  return remainingMoves[Math.floor(Math.random()*remainingMoves.length)]}

It’s simply leveraging the Math.random() method of JavaScript and returning a box based on that from the remainingMoves. So, that’s it. You’re ready to create your unbeatable tic-tac-toe. You can go ahead and write your own with the same principles or even improve over these.

You can even try to write a method that forcesTrappingMove on the opponent making your player a little more aggressive. If you got this far, thanks for your patience. Till next time!

TL;DR Writing code to create an unbeatable tic-tac-toe is simple, but coming up with the logic is what matters.