Moving the contents inside squares in a particular order

I have a question regarding moving the text to a specific square of the grid as explained below. I am working off of Paul’s CodePen which is demonstrating a drag and drop example and I’ve modified it a little bit in my JSFiddle to ask the following question:

What I’m looking for:

When a user clicks the Move Text Content! button, all the text inside the blue color cells on the left of the grid should move inside the grid. The order I want it to follow can be figured out based on the text inside the blue cell.

So, H1 Text should move inside H1 square ; H2 Text should move inside H2 square so on and so forth … H6 Text should move inside H6 square.

Once it has reached 6th square starting from row H, I would like to start filling next test from G1. Hence, G1 Text should go inside G1 square and so on and so forth …G6 Text should go inside G6 square.

The names inside blue text box is just for proper understanding purpose such that it’s easy to know where that text needs to go while figuring out a solution for this. In reality, it’s not going to be more than 8-10 characters of text.

Question:

One approach to address above requirement, that I am thnking is to define id for each and every <div class="content"></div> which corresponds to different squares and start approaching the problem from there. But I wanted to check with other people in this group to see if that’s the right direction I am going to be heading or if there’s a better way to handle what I’m trying to accomplish.

Thanks in advance.

1 Like

Have that text in each grid square all the time but hide it initially using CSS.

As @archibald mentioned above it might just be easier to hide and show the elements but assuming you are adding only some content into the cells or that you want to specify different cell etc then the following approach may be useful.

Add a data-destination to the text that specifies the cell you want it to go to. As you have 8 rows of 12 then you can simply use a number from 1 - 96 to go the appropriate cell and then you can use nth-child in the js to find that cell without needing an id on the grid cells.

For h1 that would be data-destination=“85” .

 <div data-id="1"><span data-id="1" data-destination="85" class="words">H1 Text</span></div>
 <div data-id="2"><span data-id="2" data-destination="86" class="words">H2 Text</span></div>
etc...
const myButton = document.querySelector("#move-text");
myButton.addEventListener("click", () => {
  fill();
})
function fill() {
  const cells = document.querySelectorAll("#phrase > div > span");
   var destination = 0;
  var newLoc = "";
  cells.forEach((cell, index) => {
    newLoc= document.querySelector(".item:nth-child(" + cell.dataset.destination +") .content ");
   newLoc.append(cell);
  });
}

Full demo.

1 Like

Hmm, since the list on the left hand side of the grid is not fixed and it can be max of 96 elements or 12 elements just like I had in my JsFiddle demo or anything else, I believe I would have to assign data-destination value at the time of dynamic list generation.

Could you show me the calculation for how did you calculate number 85 for H1 cell?

I believe this number will be useful to save in the database as well since I am planning to save the cell info for a particular user in case they want to resume working on the webpage later on.

Thanks!

1 Like

Yes, how else would anyone know where it should go?

All you have is text in the element. Who determines where that text should go?

You just wrote “H2 text …” in the element so it seems like you are deciding where it should go and therefore you would have no problem in applying a data-destination at the same time.

You either have to identify it specifically or else you have to have 96 items on the left automatically corresponding to a cell on the right.

That’s basic math which I’m sure you know but maybe are not thinking about it :slight_smile:

There are 96 squares in the grid. They are in rows of 12. Therefore a6 would equal cell number 6 and b6 would equal cell number 18 and c6 would equal cell 30. It’s just multiples of 12.

A1 - A12 = 1 - 12
B1 - B12 = 13 - 24
C1 - C12 = 25 - 36
D1 = D12 = 37 - 48
E1 - E12 = 49 - 60
F1 - F12 = 61 - 72
G1 - G12 = 73 - 84
H1 - H12 = 85 - 96

No magic just math :slight_smile:

I’ve added numbers in the cells so you can see more clearly.

1 Like

Yes. Here’s how the divs with data-destination looks like when I specified all 96 squares in the order I want the squares to get filled:


div data-id="1"><span data-id="1" data-destination="85" class="words">(Sq#1)</span></div>
            <div data-id="2"><span data-id="2" data-destination="86" class="words"> (Sq#2)</span></div>
            <div data-id="3"><span data-id="3" data-destination="87" class="words"> (Sq#3)</span></div>
            <div data-id="4"><span data-id="4" data-destination="88" class="words">(Sq#4)</span></div>
            <div data-id="5"><span data-id="5" data-destination="89" class="words">(Sq#5)</span></div>
            <div data-id="6"><span data-id="6" data-destination="90" class="words">(Sq#6)</span></div>
            <div data-id="7"><span data-id="7" data-destination="73" class="words">(Sq#7)</span></div>
            <div data-id="8"><span data-id="8" data-destination="74" class="words">(Sq#8)</span></div>
            <div data-id="9"><span data-id="9" data-destination="75" class="words">(Sq#9)</span></div>
            <div data-id="10"><span data-id="10" data-destination="76" class="words">(Sq#10)</span></div>
            <div data-id="11"><span data-id="11" data-destination="77" class="words">(Sq#11)</span></div>
            <div data-id="12"><span data-id="12" data-destination="78" class="words">(Sq#12)</span></div>
            <div data-id="13"><span data-id="13" data-destination="61" class="words">Square 13</span></div>
            <div data-id="14"><span data-id="14" data-destination="62" class="words">Square#14</span></div>
            <div data-id="15"><span data-id="15" data-destination="63" class="words">Square#15</span></div>
            <div data-id="16"><span data-id="16" data-destination="64" class="words">Square#16</span></div>
            <div data-id="17"><span data-id="17" data-destination="65" class="words">Square#17</span></div>
            <div data-id="18"><span data-id="18" data-destination="66" class="words">Square#18</span></div>
            <div data-id="19"><span data-id="19" data-destination="49" class="words">Square#19</span></div>
            <div data-id="20"><span data-id="20" data-destination="50" class="words">Square#20</span></div>
            <div data-id="21"><span data-id="21" data-destination="51" class="words">Square#21</span></div>
            <div data-id="22"><span data-id="22" data-destination="52" class="words">Square#22</span></div>
            <div data-id="23"><span data-id="23" data-destination="53" class="words">Square#23</span></div>
            <div data-id="24"><span data-id="24" data-destination="54" class="words">Square#24</span></div>
            <div data-id="25"><span data-id="25" data-destination="37" class="words">Square#25</span></div>
            <div data-id="26"><span data-id="26" data-destination="38" class="words">Square#26</span></div>
            <div data-id="27"><span data-id="27" data-destination="39" class="words">Square#27</span></div>
            <div data-id="28"><span data-id="28" data-destination="40" class="words">Square#28</span></div>
            <div data-id="29"><span data-id="29" data-destination="41" class="words">Square#29</span></div>
            <div data-id="30"><span data-id="30" data-destination="42" class="words">Square#30</span></div>

            <div data-id="31"><span data-id="31" data-destination="25" class="words">Square#31</span></div>
            <div data-id="32"><span data-id="32" data-destination="26" class="words">Square#32</span></div>
            <div data-id="33"><span data-id="33" data-destination="27" class="words">Square#33</span></div>
            <div data-id="34"><span data-id="34" data-destination="28" class="words">Square#34</span></div>
            <div data-id="35"><span data-id="35" data-destination="29" class="words">Square#35</span></div>
            <div data-id="36"><span data-id="36" data-destination="30" class="words">Square#36</span></div>

            <div data-id="37"><span data-id="37" data-destination="13" class="words">Square#37</span></div>
            <div data-id="38"><span data-id="38" data-destination="14" class="words">Square#38</span></div>
            <div data-id="39"><span data-id="39" data-destination="15" class="words">Square#39</span></div>
            <div data-id="40"><span data-id="40" data-destination="16" class="words">Square#40</span></div>
            <div data-id="41"><span data-id="41" data-destination="17" class="words">Square#41</span></div>
            <div data-id="42"><span data-id="42" data-destination="18" class="words">Square#42</span></div>

            <div data-id="43"><span data-id="43" data-destination="1" class="words">Square#43</span></div>
            <div data-id="44"><span data-id="44" data-destination="2" class="words">Square#44</span></div>
            <div data-id="45"><span data-id="45" data-destination="3" class="words">Square#45</span></div>
            <div data-id="46"><span data-id="46" data-destination="4" class="words">Square#46</span></div>
            <div data-id="47"><span data-id="47" data-destination="5" class="words">Square#47</span></div>
            <div data-id="48"><span data-id="48" data-destination="6" class="words">Square#48</span></div>


            <!-- Other half of 48 divs -->

            <div data-id="49"><span data-id="49" data-destination="91" class="words">Square#49</span></div>
            <div data-id="50"><span data-id="50" data-destination="92" class="words">Square#50</span></div>
            <div data-id="51"><span data-id="51" data-destination="93" class="words">Square#51</span></div>
            <div data-id="52"><span data-id="52" data-destination="94" class="words">Square#52</span></div>
            <div data-id="53"><span data-id="53" data-destination="95" class="words">Square#53</span></div>
            <div data-id="54"><span data-id="54" data-destination="96" class="words">Square#54</span></div>

            <div data-id="55"><span data-id="55" data-destination="79" class="words">Square#55</span></div>
            <div data-id="56"><span data-id="56" data-destination="80" class="words">Square#56</span></div>
            <div data-id="57"><span data-id="57" data-destination="81" class="words">Square#57</span></div>
            <div data-id="58"><span data-id="58" data-destination="82" class="words">Square#58</span></div>
            <div data-id="59"><span data-id="59" data-destination="83" class="words">Square#59</span></div>
            <div data-id="60"><span data-id="60" data-destination="84" class="words">Square#60</span></div>

            <div data-id="61"><span data-id="61" data-destination="67" class="words">Square#61</span></div>
            <div data-id="62"><span data-id="62" data-destination="68" class="words">Square#62</span></div>
            <div data-id="63"><span data-id="63" data-destination="69" class="words">Square#63</span></div>
            <div data-id="64"><span data-id="64" data-destination="70" class="words">Square#64</span></div>
            <div data-id="65"><span data-id="65" data-destination="71" class="words">Square#65</span></div>
            <div data-id="66"><span data-id="66" data-destination="72" class="words">Square#66</span></div>

            <div data-id="67"><span data-id="67" data-destination="55" class="words">Square#67</span></div>
            <div data-id="68"><span data-id="68" data-destination="56" class="words">Square#68</span></div>
            <div data-id="69"><span data-id="69" data-destination="57" class="words">Square#69</span></div>
            <div data-id="70"><span data-id="70" data-destination="58" class="words">Square#70</span></div>
            <div data-id="71"><span data-id="71" data-destination="59" class="words">Square#71</span></div>
            <div data-id="72"><span data-id="72" data-destination="60" class="words">Square#72</span></div>

            <div data-id="73"><span data-id="73" data-destination="43" class="words">Square#73</span></div>
            <div data-id="74"><span data-id="74" data-destination="44" class="words">Square#74</span></div>
            <div data-id="75"><span data-id="75" data-destination="45" class="words">Square#75</span></div>
            <div data-id="76"><span data-id="76" data-destination="46" class="words">Square#76</span></div>
            <div data-id="77"><span data-id="77" data-destination="47" class="words">Square#77</span></div>
            <div data-id="78"><span data-id="78" data-destination="48" class="words">Square#78</span></div>

            <div data-id="79"><span data-id="79" data-destination="31" class="words">Square#79</span></div>
            <div data-id="80"><span data-id="80" data-destination="32" class="words">Square#80</span></div>
            <div data-id="81"><span data-id="81" data-destination="33" class="words">Square#81</span></div>
            <div data-id="82"><span data-id="82" data-destination="34" class="words">Square#82</span></div>
            <div data-id="83"><span data-id="83" data-destination="35" class="words">Square#83</span></div>
            <div data-id="84"><span data-id="84" data-destination="36" class="words">Square#84</span></div>

            <div data-id="85"><span data-id="85" data-destination="19" class="words">Square#85</span></div>
            <div data-id="86"><span data-id="86" data-destination="20" class="words">Square#86</span></div>
            <div data-id="87"><span data-id="87" data-destination="21" class="words">Square#87</span></div>
            <div data-id="88"><span data-id="88" data-destination="22" class="words">Square#88</span></div>
            <div data-id="89"><span data-id="89" data-destination="23" class="words">Square#89</span></div>
            <div data-id="90"><span data-id="90" data-destination="24" class="words">Square#90</span></div>

            <div data-id="91"><span data-id="91" data-destination="7" class="words">Square#91</span></div>
            <div data-id="92"><span data-id="92" data-destination="8" class="words">Square#92</span></div>
            <div data-id="93"><span data-id="93" data-destination="9" class="words">Square#93</span></div>
            <div data-id="94"><span data-id="94" data-destination="10" class="words">Square#94</span></div>
            <div data-id="95"><span data-id="95" data-destination="11" class="words">Square#95</span></div>
            <div data-id="96"><span data-id="96" data-destination="12" class="words">Square#96</span></div>

As can be seen the sequence for data-destination is from 85 to 90 for first 6 text. 73-78 for next 6 texts and so on and so forth.

Isn’t it going to be complex at the time of generating these divs with data-destination at the time of dynamically generating the list? For example, if I have a list of 9 texts. So the first five would go with the location with data-destination value of 85-90 and the remaining 3 would go with the location with data-destination value of 73-75.

The approach I was thinking of was to have predefined arrays for all the values.

For example,

let array1 = [“85”, “86”,“87”,“88”,“89”,“90”];

let array2 = [“73”,“74”,“75”,“76”,“77”,“78”];

and so on and so forth .

So while generating the dynamic list, I could use (in my example of 9 texts) all the six values from array1 and first 3 values from array2. Does this approah sound like I’m heading in right direction?

Not to me :slight_smile:

Why are they all out of order?

If you are filling all squares then why don’t you fill them in order? No need for any numbers if you are filling all squares as you just put the item in the right element to begin with!

So who decided that they should go in that position? What method is making that choice? Whatever method you use to make that choice could simply at the same time add the data-destination. I expect you are doing this in PHP or something so I don’t see the problem or difficulty or the need for multiple arrays of stuff. If these items are save in a database or whatever then you could save an index or something that points to the square where the element needs to reside.

As the side list is dynamically created and there is the expectation that for every position in the list there is predefined position in the grid I would fill the list and at the same time fill the table cell.
Use css to show the list and hide the table contents, then use the button to hide the list and unhide the table.
Or am I missing something?

1 Like

Because I have a requirement to fill it in such a way. Here is a screenshot which shows the numbering inside the cell and number 1 is starting from H1, number 2 starts from H2 and so on and so forth.

MicrosoftTeams-image (1)

It will be in JSP but I’m trying to handle it via Javascript as of now.

Where are those data numbers coming from? Why would you want the first item of data to be at H1 and then suddenly at h7 you want the 49th piece of data.

Why don’t you simply order them linearly to start with. You can output whatever data you like but you’d just start logically.

Or do you have a list that you can’t change?

There probably is a mathematical formula to work it all out with a few loops but there’s no point in writing it until we are certain what you want. You can with CSS use the order property to move things around but you’d have to hard code it anyway so you may as well use the data-destination method.

I think I’d need to know a bit more why your data is structured this way. :slight_smile:

Sure. The whole grid thing is going to serve as a map for a hardware device that has plates(similar to square) used in a laboratory. So lab people put the reagents(the name of the reagent is equivalent to the text that we are putting in each square in our scenario ) to test in a certain order. So whatever reagents the laboratory people get, they have to put it in a certain order. This order is what the image I shared in my last post. The number written in that image is just to show what order they follow as far as putting the reagent is concerned. Hence, the order is followed in such a way that first six reagents will be put on the plate(square of the grid) starting from H1 to H6 and then the order for next reagents ( if there are six more followed after first six) will be G7 to G12 and so on and so forth.

Why do you need the drag and drop if you are applying them all with the button?

… and if that’s the case why don’t you just display it by default?

There are still a lot of loose ends here :slight_smile:

Nonetheless the logic of your ordering can be done with JS and would look like this.

It just creates an array of numbers that match your logic.

function reArrange() {
  var limit1 = 85;
  var limit2 = 91;

  for (let loop = 0; loop < 8; loop++) {
    for (let i = 0; i < 6; i++) {
      reOrder.push(limit1 + i);
    }
    limit1 = limit1 - 12;
  }
  for (let loop = 0; loop < 8; loop++) {
    for (let j = 0; j < 6; j++) {
      reOrder.push(limit2 + j);
    }
    limit2 = limit2 - 12;
  }
}
reArrange();

I don’t really see how that is any easier than just adding the data-destination to the html to start with but it might be useful as an example. Also bear in mind my JS is very basic and that nested loop is probably not the right way to do this.:wink:

2 Likes

Hey @PaulOB,

Thanks for sharing your approach. I have a few questions about your approach. As I can see the reOrder array contains the numerical pattern (after the reArrange function is called on page load) that I want my blue button text to get filled in the grid, which works fine.

I’ve got a requirement where I want to start filling the cells in the same pattern but the starting point could be (let’s say for example, 73 number grid or any other grid for example) different. So I was able to tweak the limit1 variable inside reArrange() function to make it start from where I want. However, in the user interface, user is not aware what number 73 or 96 or anything means and I may get a user input saying that user would like to start filling up from G1 (which has a numerical value 73) or F5 (which has a numerical value of 65).

Is there a better way to determine this value based on the user input like G1 or F5 etc?

I can think of few solutions like:

  1. I can hard code all these numbers in a variable , for example const G1 = 73, const F5 = 65 etc and once user provides a value to me (probably from an input text box, I can look for the value set the limit1 variable to start from that)

Or

  1. I can create a drop-down list with value 73 and text showing G1 and have the user select one of the 96 values.

Both approach sounds somewhat inefficient to me as I’m hardcoding a lot of things here but wanted to get your thoughts on this.

In your drawing (post #9) G1 = 7 and F5 = 17 so I can’t really work out where you are going with this:)

According to your original grid (post #9) the cell can be worked out mathematically. Here’s a basic codepen test (with no error checking) that shows the cell number based on a row and column selection. It could be improved and shortened but the logic is much the same. Of course its based on that grid you posted.

Does that mean you would start at cell 73 and then fill to cell 96 and them go back to fill from cell 1 ?

I meant 7 and 17 for G1 and F5 and got it mixed up with the cell value it looks like.

Yes, depending upon what starting square location the user provides. If there are only 20 items then it won’t go until 96 in this case. But the user can come back later and select another starting location so that other remaining empty locations can be filled up.

It looks like I would just have to hard code the letters A-H just like you did in your switch and case.

Further down the line, I am also going to look to see if one can drag the content of the cell and move it to another empty square.

1 Like

Hi Paul,

I was testing some variations in the code by changing the limit1 value inside the reArrange() method. So let’s say I want to start putting the blue text boxes from cell A4 and the value of this cell is 46 based on the example you showed in post #15. I noticed that it completely messes up the movement after clicking Move Text Content! button. For example, I have this JSFiddle with limit1 = 46 and limit2 = 91. And it doesn’t seem to be starting from A4 as you can see. The reOrder array seems to be getting negative values and I suppose that’s the reason?

Thanks,

Edited: I think it is starting as expected but it looks like the approach in post #15 needs to be modified such that if I supply A4, I get number 4 back. If I supply D10, I get number 46 back ?

I think it needs to be based on the cell numbers showing on the corner of the grid as the Move Text button is operating based off of that.

That doesn’t make sense as you are not filling in that order. Those numbers in the cells are just a straight numerical order.

Really you are just concerned with filling the array and let the existing routine do its job.

I don;t have time for a full example but imagine you wanted to start the items from a grid number you could do this:

No need to change the rearrange function as all that does is reference the cells in the right order. I just started pasting the content in the content at the cell that was entered.

There is no error checking as this is just a quick proof of concept and what you’d need to do is to check that the number of items you want to paste doesn’t exceed the end limit (assuming you aren’t starting at h1). If the number is greater than 96 then those remaining items should be pasted into the cells starting from the beginning. (assuming that was your intention)

Ok, I quickly added that in to the code so that now you can specify any square and it will wrap to the start if the end is reached.

Again there is no error checking on the inputs as you should limit input for a - h and 1 -12 only which you can do in your own time :slight_smile:

1 Like

Very much a work inprogress — tinkering with your code :).

Wrapping the inputs in a form element and using submit will allow you to take advantage of HTML’s form validation and will prevent you clicking on Calculate without valid inputs. Note, it’s not full proof as you can always change the HTML in devtools, but it’s a move in the right direction.

<div id='phrase'>
  <form id='calculate-position'>
    <label for='rowInput'>Enter a row A - H</label>
    <input type='text' id='rowInput' pattern='[a-hA-H]' placeholder='Enter Row' required>
    <label for='colInput'>Enter a column 1 - 12</label>
    <input type='number' id='colInput' min='1' max='12' pattern='[0-9]' placeholder=' Enter Column' required>
    <button id='calculate'>Calculate</button>
  </form>
  <output class='target'>The cell number is <b id='cellnum'>?</b></output>
  <div id='words'>
    <button type='button' id='move-text' type='button'>Move Text Content!</button>
    <!-- remove whitespace from  inside div html and then we can use :empty in css to change background -->
    <div data-id='01'><span class='words' data-id='01'>H1 text</span></div>
    <div data-id='02'><span class='words' data-id='02'>H2 text</span></div>
    ...
    <div data-id='10'><span class='words' data-id='10'>G4 text</span></div>
  </div>
</div>

Some more tinkering with the get cell number section.

Instead of using a long list of switch cases the start can be calculated using charCodeAt

function calcRowFromChar(char, start = 42, step = -6) {
  // e.g. 'b' → upperCase 'B' → charCode is 66 → minus 65 → 1
  const charPosInAlphabet = char.toUpperCase().charCodeAt(0) - 65;
  // e.g. 42 + 1 * -6 = 36
  return start + charPosInAlphabet * step;
}

calcRowFromChar('a') // 42
calcRowFromChar('c') // 30
calcRowFromChar('h') // 0

The global startPos was bugging me a bit. It had me scrolling up and down the page to see where it was being used. I could be wrong but it made sense to me to pass that directly to the fill function as an argument instead.

The issue though was how to set startPos with the calculate handler and make it available to the movebutton handler without making it a global. I opted to wrap it inside of a module pattern which returns the two handlers.

So here is a bit of refactoring, I hope you don’t mind.

function calcRowFromChar(char, start = 42, step = -6) {
  // e.g. 'a' → upperCase 'A' → charCode is 65 → minus 65 → 0
  const charPosInAlphabet = char.toUpperCase().charCodeAt(0) - 65;
  return start + charPosInAlphabet * step;
}

// A module pattern to keep startPos out of the
// global context and only where it is needed
function createHandlers() {
  // startPos is local to createHandlers and only
  // available to the returned handlers
  let startPos = 1;

  return {
    calculateStart(event) {
      // prevent actually submitting the form
      event.preventDefault();
      const start = 42
      // we can access the form elements directly from the form, without querySelecting
      const { rowInput, colInput } = event.target.elements;
      const rowNum = calcRowFromChar(rowInput.value, start);
      const colNum = Number(colInput.value);

      startPos = (colNum < 7)
        ? rowNum + colNum
        : rowNum + start + colNum;

      document.querySelector('#cellnum').textContent = startPos;
    },

    moveToGrid(event) {
      // disable button
      event.target.classList.add('disabled');
      // pass in startPos to fill function instead
      fill(startPos);
    }
  };
}

const handlers = createHandlers();

const calculateButton = document.querySelector('#calculate-position');
calculateButton.addEventListener('submit', handlers.calculateStart);

const moveTextBtn = document.querySelector('#move-text');
moveTextBtn.addEventListener('click', handlers.moveToGrid, { once: true });

fill would need to be ammended accordingly

function fill(startPos) {
   ...
});

A codepen, with these few ammends

1 Like