Changing the order of a CSS Grid in Javascript

Hey all, I’m trying to change the format of a CSS grid in javascript by changing the order element of div elements. Whenever the button is clicked, a function is supposed to run that changes the order of box1 to 4. as of now, it spits out Uncaught TypeError: Cannot read properties of null (reading ‘style’)
at boxSort (script.js:44:11)

Here’s what my code is so far:

HTML portion:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, World!</title>
    <link rel="stylesheet" href="styles.css" />
    <style>
      #box1 { order: 3;} 
      #box2 { order: 1;} 
      #box3 { order: 2;} 
      #box4 { order: 4;} 
      #box5 { order: 5;} 
      #box6 { order: 6;} 
    </style>
  </head>
  <body>
      <script src="script.js"></script>
      <h1 class="title">Hello World! </h1>
      <button id="sortButton" onclick="boxSort()"> Sort Boxes </button>
      <div class ="grid-container" id = "divGrid">
        <div id ="box1"  onclick="counter1()" >Box 1 </div>
        <div id ="box2"  onclick="counter2()"> Box 2</div>
        <div id ="box3"  onclick="counter3()"> Box 3</div>
        <div id ="box4"  onclick="counter4()"> Box 4</div>
        <div id ="box5"  onclick="counter5()"> Box 5</div>
        <div id ="box6"  onclick="counter6()"> Box 6</div>
      </div>
     
  </body>
</html>

CSS:

body{
  padding: 25px;
}
.title {
	color: #5C6AC4;
}
#sortButton {
  border: 1px solid black ;
  color: black;
  padding: 16px 32px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
  margin: 4px 2px;
  cursor: pointer;
}
.grid-container {
  display: grid;
  height: auto;
  align-content: center;
  grid-template-columns: auto;
  column-gap: 10px;
  row-gap: 10px;
  background-color: #2196F3;
  padding: 10px;
}
.grid-container > div {
  background-image: url("background.jpeg"); 
  background-color: #cccccc; 
  height: 100px; 
  background-position: center; 
  background-repeat: no-repeat; 
  background-size: cover; 
  text-align: center;
  padding: 20px 0;
  font-size: 30px;
  cursor: pointer;
}


.firstBox { order: 1;}
.secondBox { order: 2;}
.thirdBox { order: 3;}
.fourthBox { order: 4;}
.fifthBox { order: 5;}
.sixthBox { order: 6;}




@media only screen and (min-width: 1000px) {
  .grid-container {
    grid-template-columns: auto auto auto;
  }

}

@media only screen and (max-width: 1000px) {
  .grid-container {
    grid-template-columns: auto auto;
  }
}
@media only screen and (max-width: 680px) {
  .grid-container {
    grid-template-columns: auto;
  }
}

JavaScript:

let count1 = 0; 
let count2 = 0; 
let count3 = 0; 
let count4 = 0; 
let count5 = 0; 
let count6 = 0; 

const box1Div = document.getElementById("box1");
const box2Div = document.getElementById("box2");
const box3Div = document.getElementById("box3");
const box4Div = document.getElementById("box4");
const box5Div = document.getElementById("box5");
const box6Div = document.getElementById("box6");
const divStyles = document.getElementById("divGrid").classList;


function counter1() {
  count1 += 1;
  alert("Box 1 was clicked a total of " + count1 + " times.");
}
function counter2() {
  count2 += 1;
  alert("Box 2 was clicked a total of " + count2 + " times.");
}
function counter3() {
  count3 += 1;
  alert("Box 3 was clicked a total of " + count3 + " times.");
}
function counter4() {
  count4 += 1;
  alert("Box 4 was clicked a total of " + count4 + " times.");
}
function counter5() {
  count5 += 1;
  alert("Box 5 was clicked a total of " + count5 + " times.");
}
function counter6() {
  count6 += 1;
  alert("Box 6 was clicked a total of " + count6 + " times.");
}
function boxSort() {
  const boxArray = [count1, count2, count3, count4, count5, count6];
  boxArray.sort(function(a, b){return b - a});

  box1Pos = 4; /* boxArray.indexOf(count1) + 1; */
  
  document.box1Div.style.order = "4";
  
  
}

Not exactly sure what to do here. I tried adding a class to the box1 element with the order value of 1, but to no avail(it gave me a similar error message).

Also please ignore some of the code that does nothing, as of now I am trying various attempts to solve this problem and would be open to a variety of solutions. I would like to keep css grid(no flexbox, libraries, or media queries) and keep the program fairly simple. Thanks!

Hi,

To answer your question directly, the problem is this line:

document.box1Div.style.order = "4";

document.box1Div is undefined so your script will break.

To make it work do this:

box1Div.style.order = "4";
box4Div.style.order = "3";

If you change the order of box1Div to 4, you will also need to change the element which originally had the order of 4 (boxDiv3 in this case), to something else, otherwise nothing will happen.

Now, when the button is clicked, box 1 and box 4 will swap position.


On a separate note, there is quite a bit that could be improved with the code. Would you like a couple of pointers on what you could do better?

4 Likes

Thanks so much for responding! Unfortunately, this solution does not work. Now when the function boxSort() is called the code

 box1Div.style.order = "4";
 box4Div.style.order = "3";

is run, but it still gives the same error message of
Uncaught TypeError: Cannot read properties of null (reading ‘style’)
at boxSort (script.js:47:11)
at HTMLButtonElement.onclick (42fbjarkr_42fbupysa/:19:51)
boxSort @ script.js:47
onclick @ 42fbjarkr_42fbupysa/:19

I’ve tried multiple solutions, but nothing seems to work. Do you have any other idea what might be causing this issue?

Additionally, I would love some pointers to improve the code. I’m fairly new to programming so any help would be greatly appreciated.

Then there is something else at play. Do you have a link to a page where we can see the error?

Let’s get the problem at hand solved first :grinning:

Move your script to the end of the html just before the closing tag. Otherwise it runs before the html has been loaded and finds nothing.

   <script src="script.js"></script>
 </body>
</html>
1 Like

Oh yeah, good catch.
I had done that on my PC, but forgot to mention it in my answer.

You can copy/paste this code:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Hello, World!</title>
    <style>
      body{
        padding: 25px;
      }

      .title {
        color: #5C6AC4;
      }

      #sortButton {
        border: 1px solid black ;
        color: black;
        padding: 16px 32px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 4px 2px;
        cursor: pointer;
      }
      
      .grid-container {
        display: grid;
        height: auto;
        align-content: center;
        grid-template-columns: auto;
        column-gap: 10px;
        row-gap: 10px;
        background-color: #2196F3;
        padding: 10px;
      }

      .grid-container > div {
        background-image: url("background.jpeg");
        background-color: #cccccc;
        height: 100px;
        background-position: center;
        background-repeat: no-repeat;
        background-size: cover;
        text-align: center;
        padding: 20px 0;
        font-size: 30px;
        cursor: pointer;
      }

      .firstBox { order: 1;}
      .secondBox { order: 2;}
      .thirdBox { order: 3;}
      .fourthBox { order: 4;}
      .fifthBox { order: 5;}
      .sixthBox { order: 6;}

      @media only screen and (min-width: 1000px) {
        .grid-container {
          grid-template-columns: auto auto auto;
        }

      }

      @media only screen and (max-width: 1000px) {
        .grid-container {
          grid-template-columns: auto auto;
        }
      }

      @media only screen and (max-width: 680px) {
        .grid-container {
          grid-template-columns: auto;
        }
      }

      #box1 { order: 3;}
      #box2 { order: 1;}
      #box3 { order: 2;}
      #box4 { order: 4;}
      #box5 { order: 5;}
      #box6 { order: 6;}
    </style>
  </head>
  <body>
      <h1 class="title">Hello World! </h1>
      <button id="sortButton" onclick="boxSort()"> Sort Boxes </button>
      <div class ="grid-container" id = "divGrid">
        <div id ="box1"  onclick="counter1()" >Box 1 </div>
        <div id ="box2"  onclick="counter2()"> Box 2</div>
        <div id ="box3"  onclick="counter3()"> Box 3</div>
        <div id ="box4"  onclick="counter4()"> Box 4</div>
        <div id ="box5"  onclick="counter5()"> Box 5</div>
        <div id ="box6"  onclick="counter6()"> Box 6</div>
      </div>
    <script>
      let count1 = 0;
      let count2 = 0;
      let count3 = 0;
      let count4 = 0;
      let count5 = 0;
      let count6 = 0;

      const box1Div = document.getElementById("box1");
      console.log(box1Div);
      const box2Div = document.getElementById("box2");
      const box3Div = document.getElementById("box3");
      const box4Div = document.getElementById("box4");
      const box5Div = document.getElementById("box5");
      const box6Div = document.getElementById("box6");
      const divStyles = document.getElementById("divGrid").classList;

      function counter1() {
        count1 += 1;
        alert("Box 1 was clicked a total of " + count1 + " times.");
      }
      function counter2() {
        count2 += 1;
        alert("Box 2 was clicked a total of " + count2 + " times.");
      }
      function counter3() {
        count3 += 1;
        alert("Box 3 was clicked a total of " + count3 + " times.");
      }
      function counter4() {
        count4 += 1;
        alert("Box 4 was clicked a total of " + count4 + " times.");
      }
      function counter5() {
        count5 += 1;
        alert("Box 5 was clicked a total of " + count5 + " times.");
      }
      function counter6() {
        count6 += 1;
        alert("Box 6 was clicked a total of " + count6 + " times.");
      }

      function boxSort() {
        const boxArray = [count1, count2, count3, count4, count5, count6];
        boxArray.sort(function (a, b) {
          return b - a;
        });

        box1Pos = 4; /* boxArray.indexOf(count1) + 1; */

        box1Div.style.order = "4";
        box4Div.style.order = "3";
      }
    </script>
  </body>
</html>
1 Like

PaulOB’s advice worked! Now I’ve run into a bit of a different problem though. The actual program is supposed to take the amount of clicks each box has received and sort them in descending order. I was able to sucessfully do this and put them into an array and sort them, but for some reason it doesn’t set the order properly. For example, the code snippet

  box1Div.style.order = "1";
  box2Div.style.order = "2";
  box3Div.style.order = "3";
  box4Div.style.order = "4";
  box5Div.style.order = "5";
  box6Div.style.order = "6";

actually sets the order sucessfully, but when I try to replace 1 with the variable box1Pos, it doesn’t set the order.
Here is the JavaScript for what I have so far.

 let box1Pos =  boxArray.indexOf(count1) + 1;
  let box2Pos =  boxArray.indexOf(count2) + 1;
  let box3Pos =  boxArray.indexOf(count3) + 1;
  let box4Pos =  boxArray.indexOf(count4) + 1;
  let box5Pos =  boxArray.indexOf(count5) + 1;
  let box6Pos =  boxArray.indexOf(count6) + 1;
  console.log(box1Pos);
  console.log(box2Pos);
  console.log(box3Pos);
  console.log(box4Pos);
  console.log(box5Pos);
  console.log(box6Pos);
  
  box1Div.style.order = "box1Pos";
  box2Div.style.order = "box2Pos";
  box3Div.style.order = "box3Pos";
  box4Div.style.order = "box4Pos";
  box5Div.style.order = "box5Pos";
  box6Div.style.order = "box6Pos";

The console.log() statements assure that the array and variables are being set and sorted correctly, and changing the order works when you type in an actual number, but for some reason my program isn’t working when I try to set the order to a previously defined variable. Thanks! I would greatly appreciate any pointers.

If it helps, this is the prompt for the part of the project I’m having issues with:
“Second, add a button that when clicked, the boxes will be sorted based on how many times they have been clicked in descending order. If two or more boxes have the same number of clicks, then their order should remain unchanged when the sort button is clicked.”

Remove the quote marks as that’s a string and not a variable :slight_smile:

I’ll let Jim explain the difference :wink:

Thank you so much! I understand the difference between a string and a variable, this works great(I just made a simple syntax mistake that I’m glad you pointed out). The program works great now.

The program seems slightly inefficient with all the separate functions and variables, is there a way to make this more efficient. I’m also not sure if the program will keep the two or more boxes with the same number of clicks’ order relative to each other unchanged when the sort button is clicked.

Additionally, is there any other changes I can make to make it easier to read/more efficient? Thanks!

A lot of that is repetitive and could be managed in a single loop to set and retrieve everything as required. Also if you added a class to the boxes html then you could add event listeners to them all from the js rather than using an onclick in the html (or add an event listener to the parent and detect which child was clicked.

However its best if you wait for @James_Hibbard to reply as he has already mentioned he’s willing to help as he will give expert advice :slight_smile:

2 Likes

Yeah, happy to help. Could you post the code you have and are now working with, then we can look at what could be improved.

Absolutely!
Here’s the HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Impact Studio Assignment</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
      <script src="script.js"></script>
      <h1 class="title"> Impact Studio Assignment </h1>
      <h2 style="text-align: center;">Trew Smith</h2>
      <div class ="grid-container" id = "divGrid">
        <!--Seperate functions and variables for each box allow thea amount of clicks to be tracked independently of the parent div element and used multiple times in the JavaScript-->
        <div id ="box1" style ="order: 3;"  onclick="counter1()" >Box 1 </div>
        <div id ="box2" style ="order: 1;" onclick="counter2()"> Box 2</div>
        <div id ="box3" style ="order: 2;"  onclick="counter3()"> Box 3</div>
        <div id ="box4" style ="order: 4;" onclick="counter4()"> Box 4</div>
        <div id ="box5" style ="order: 5;" onclick="counter5()"> Box 5</div>
        <div id ="box6" style ="order: 6;"  onclick="counter6()"> Box 6</div>
      </div>
      <button id="sortButton" onclick="boxSort()"> Sort Boxes </button>
     
  </body>
</html>

CSS:

body{
  padding: 25px;
}
.title {
	color: #5C6AC4;
	text-align: center;
}
#sortButton {
  border: 1px solid black ;
  color: black;
  padding: 16px 32px;
  text-align: center;
  text-decoration: none;
  display: block;
  font-size: 16px;
  margin: 4px auto;
  cursor: pointer;
}
.grid-container {
  display: grid;
  height: auto;
  align-content: center;
  grid-template-columns: auto;
  column-gap: 10px;
  row-gap: 10px;
  background-color: #2196F3;
  padding: 10px;
  grid-template-columns: repeat(3, auto);
}
.grid-container > div {
  background-image: url("background.jpeg"); 
  background-color: #cccccc; 
  height: 100px; 
  background-position: center; 
  background-repeat: no-repeat; 
  background-size: cover; 
  text-align: center;
  padding: 20px 0;
  color: white;
  font-size: 30px;
  font-family: Monaco, "Lucida Console", "Courier New", monospace;;
  cursor: pointer;
}

/*Set the default orientation of the boxes, changed later in JavaScript upon 'sort' button click */
#box1 {order: 1;}
#box2 {order: 2;}
#box3 {order: 3;}
#box4 {order: 4;}
#box5 {order: 5;}
#box6 {order: 6;}

/* Changing the amount of columns based on the screen dimensions;
a media screen  is not needed for a minimum width of 1000px as the default amount of columns is set to be 3 earlier in the code */
@media only screen and (max-width: 1000px) {
  .grid-container {
    grid-template-columns: repeat(2, auto);
  }
}
@media only screen and (max-width: 680px) {
  .grid-container {
    grid-template-columns: auto;
  }
}

JavaScript:

let count1 = 0; 
let count2 = 0; 
let count3 = 0; 
let count4 = 0; 
let count5 = 0; 
let count6 = 0; 


function counter1() {
  count1 += 1;
  alert("Box 1 was clicked a total of " + count1 + " times.");
}
function counter2() {
  count2 += 1;
  alert("Box 2 was clicked a total of " + count2 + " times.");
}
function counter3() {
  count3 += 1;
  alert("Box 3 was clicked a total of " + count3 + " times.");
}
function counter4() {
  count4 += 1;
  alert("Box 4 was clicked a total of " + count4 + " times.");
}
function counter5() {
  count5 += 1;
  alert("Box 5 was clicked a total of " + count5 + " times.");
}
function counter6() {
  count6 += 1;
  alert("Box 6 was clicked a total of " + count6 + " times.");
}
function boxSort() {
  //Declaring the boxes in JavaScript is done during this function to allow the document to fully load, allowing us to change the order of the element.
  //You could also declare these earlier and call the JavaScript later in the HTML code
  box1Div = document.getElementById("box1");
  box2Div = document.getElementById("box2");
  box3Div = document.getElementById("box3");
  box4Div = document.getElementById("box4");
  box5Div = document.getElementById("box5");
  box6Div = document.getElementById("box6");
  boxArray = [count1, count2, count3, count4, count5, count6];
  boxArray.sort(function(a, b){return b - a});

  const box1Pos =  boxArray.indexOf(count1) + 1;
  const box2Pos =  boxArray.indexOf(count2) + 1;
  const box3Pos =  boxArray.indexOf(count3) + 1;
  const box4Pos =  boxArray.indexOf(count4) + 1;
  const box5Pos =  boxArray.indexOf(count5) + 1;
  const box6Pos =  boxArray.indexOf(count6) + 1;

  
  box1Div.style.order = boxArray.indexOf(count1) + 1;
  box2Div.style.order = boxArray.indexOf(count2) + 1;
  box3Div.style.order = boxArray.indexOf(count3) + 1;
  box4Div.style.order = boxArray.indexOf(count4) + 1;
  box5Div.style.order = boxArray.indexOf(count5) + 1;
  box6Div.style.order = boxArray.indexOf(count6) + 1;
  
  
  
}

Thank you so much for offering to help!

A quick note- the order is only declared in the CSS file now, previously it was both in the HTML and CSS.

Additionally, if two or more boxes have the same number of clicks, then their order(relative to each other) should remain unchanged when the sort button is clicked, but the program as of puts(as an example) box 4 ahead of box5 even if box5 was previously ahead of box 4.

Thanks!

OK, well first thing to do is to sort out all of the redundant variables and counter functions.

Change your code like this and test that it works:

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Impact Studio Assignment</title>
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <h1 class="title"> Impact Studio Assignment </h1>
    <h2 style="text-align: center;">Trew Smith</h2>

    <div class ="grid-container" id="divGrid">
      <div id ="box1" style ="order: 3;">Box 1</div>
      <div id ="box2" style ="order: 1;">Box 2</div>
      <div id ="box3" style ="order: 2;">Box 3</div>
      <div id ="box4" style ="order: 4;">Box 4</div>
      <div id ="box5" style ="order: 5;">Box 5</div>
      <div id ="box6" style ="order: 6;">Box 6</div>
    </div>

    <button id="sortButton" onclick="boxSort()"> Sort Boxes </button>

    <script>
      const count = {};
      const boxes = document.querySelectorAll('[id^="box"');

      function handleBoxClick(){
        const id = this.id;

        if (count[id]) {
          count[id] += 1;
        } else {
          count[id] = 1;
        }

        alert (`Box ${id.replace('box', '')} was clicked a total of ${count[id]} times.`)
      }

      boxes.forEach((box) => {
        box.addEventListener('click', handleBoxClick);
      });
    </script>
  </body>
</html>

I haven’t touched the sort function, but the counters should all work like before.
Have a look at the code and let me know if there is anything you don’t understand, or if there is anything you would like me to explain more.
Also, note that it is a good practice to have your JavaScript right before the closing body tag.

2 Likes