I've been trying to make a simple grid system with pawns (chess-like) for my dungeons and dragons sessions using jquery sortable. I am using laravel with blade for a project.
So in the code below, (which was given to me by another question) it creates a grid of certain area on which the player can freely move the pawns. I made it so an ajax request can save each of the pawn's coordinates to a database in the form of 0,1 or 3,4 etc.
Generally everything works but the problem is that I cannot programmatically set the position of each pawn. This is used not only to keep the positions after page refresh but it is used to be able to make the pawn positions visible to other people using the website.
So here is the code I got so far:
let gridSize = 7,
//characters array is empty usually because it gets all the pawn infos from the db
characters = [ {"id" : "1" , "name" : "Test", "hp" : "112"}
];
$('.grid')
.css(`grid-template-columns`, `repeat(${gridSize}, 4rem)`)
.html([...Array(gridSize**2)].map(() => '<div class="cell"></div>').join(''));
$('.characters')
.html(characters.map((character) =>
`<div class="character"><div class="stats" id ='${character.id}'><p>${character.name}</p><p>hp: ${character.hp}</p></div></div>`).join(``))
.add('.grid .cell')
.sortable({
connectWith: '.characters, .grid .cell',
cursor: 'grabbing',
receive: (e, ui) => {
if(ui.item.parent().hasClass('cell')) {
let $cell = $(e.target),
$character = ui.item,
$characters = $cell.find('.character').not($character),
coords = [$cell.index() % gridSize, Math.floor($cell.index() / gridSize)];
coords = coords.toString();
let characterId = $character.find('.stats').attr('id');
updateCharacterPosition(characterId, coords)//writes the new pawn's position to the db
}
}
});
function updateCharacterPosition(characterId, coords) {
$.ajax({
url: "/coords/" + characterId,
method: 'PATCH',
data: {
_token: $('meta[name="csrf-token"]').attr('content'),
newcoords: coords
},
success: function(response) {
console.log('Character position updated successfully');
},
error: function(xhr, status, error) {
//It will error because the controller is not present.
}
});
}
.grid{
border: 2px solid gray;
display: grid;
width: fit-content;}
.grid .cell{
align-items: center;
aspect-ratio: 1 / 1;
border: 1px solid lightgray;
display: flex;
justify-content: center;
overflow: visible;}
.grid .cell:has(.character):hover{
border-width: 2px;
box-sizing: border-box;}
.characters{
border: 2px solid gray;
display: flex;
gap: 1rem;
margin-top: 1rem;
min-height: 3rem;
min-width: 6rem;
padding: 1rem;
width: fit-content;}
.character{
align-items: end;
background-color: #000000;
border-radius: 50%;
color: #ffffff;
display: flex;
height: 3rem;
justify-content: center;
text-align: center;
width: 3rem;}
.character .stats{
background-color: inherit;
padding: 4px;}
.character p{
font-size: .66rem;
margin: 0;
white-space: nowrap;}
<div class="grid"></div>
<div class="characters"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
First, let's see the example:
let gridSize = 7,
//characters array is empty usually because it gets all the pawn infos from the db
characters = [ {"id" : "1" , "name" : "Test", "hp" : "112"}
];
$('.grid')
.css(`grid-template-columns`, `repeat(${gridSize}, 4rem)`)
.html([...Array(gridSize**2)].map((i, j) => '<div class="cell">' + (((j % gridSize === 3) && (parseInt(j / gridSize) === 2)) ? `<div class="character ui-sortable-handle" style=""><div class="stats" id="1"><p>Test</p><p>hp: 112</p></div></div>` : "") + '</div>').join(''));
$('.characters')
.html(characters.map((character) =>
`<div class="character"><div class="stats" id ='${character.id}'><p>${character.name}</p><p>hp: ${character.hp}</p></div></div>`).join(``))
.add('.grid .cell')
.sortable({
connectWith: '.characters, .grid .cell',
cursor: 'grabbing',
receive: (e, ui) => {
if(ui.item.parent().hasClass('cell')) {
let $cell = $(e.target),
$character = ui.item,
$characters = $cell.find('.character').not($character),
coords = [$cell.index() % gridSize, Math.floor($cell.index() / gridSize)];
coords = coords.toString();
let characterId = $character.find('.stats').attr('id');
updateCharacterPosition(characterId, coords)//writes the new pawn's position to the db
}
}
});
function updateCharacterPosition(characterId, coords) {
$.ajax({
url: "/coords/" + characterId,
method: 'PATCH',
data: {
_token: $('meta[name="csrf-token"]').attr('content'),
newcoords: coords
},
success: function(response) {
console.log('Character position updated successfully');
},
error: function(xhr, status, error) {
//It will error because the controller is not present.
}
});
}
.grid{
border: 2px solid gray;
display: grid;
width: fit-content;}
.grid .cell{
align-items: center;
aspect-ratio: 1 / 1;
border: 1px solid lightgray;
display: flex;
justify-content: center;
overflow: visible;}
.grid .cell:has(.character):hover{
border-width: 2px;
box-sizing: border-box;}
.characters{
border: 2px solid gray;
display: flex;
gap: 1rem;
margin-top: 1rem;
min-height: 3rem;
min-width: 6rem;
padding: 1rem;
width: fit-content;}
.character{
align-items: end;
background-color: #000000;
border-radius: 50%;
color: #ffffff;
display: flex;
height: 3rem;
justify-content: center;
text-align: center;
width: 3rem;}
.character .stats{
background-color: inherit;
padding: 4px;}
.character p{
font-size: .66rem;
margin: 0;
white-space: nowrap;}
<div class="grid"></div>
<div class="characters"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
Now, the explanation:
.html([...Array(gridSize**2)].map((i, j) => '<div class="cell">' + (((j % gridSize === 3) && (parseInt(j / gridSize) === 2)) ? `<div class="character ui-sortable-handle" style=""><div class="stats" id="1"><p>Test</p><p>hp: 112</p></div></div>` : "") + '</div>').join(''));
Here, the .html()
specifies what needs to be in the HTML and we pass a text to it. The text is generated by the creation of an array of the size of gridSize * gridSize
and mapping each element to the HTML template we want. If j
is to be at the column of 3 and the row of 2 (you can use custom, dynamic values loaded from the database), then we put in the inner template. Otherwise we don't. Once we generated each template for the array, we join them into a string and pass this string as a raw HTML to the html()
function.