Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback #1

Open
wants to merge 28 commits into
base: feedback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c74b329
Setting up GitHub Classroom Feedback
github-classroom[bot] Mar 9, 2024
a5d93c1
add deadline
github-classroom[bot] Mar 9, 2024
d4473d3
Create index.html
adeledsg Mar 9, 2024
a325e63
adding my files
adeledsg Mar 10, 2024
4b881cb
Added image folder, created individual divs for each piece using js. …
adeledsg Mar 19, 2024
bf96831
changed puzzle, made them all fit for 100vh
adeledsg Mar 22, 2024
1659eb8
created drop zones and drag and drop events. Touch event not working
adeledsg Apr 5, 2024
d19b726
Merge pull request #2 from Birkbeck2/initial-set-up
adeledsg Apr 5, 2024
32566dc
Rename puzzle1.html to index.html
adeledsg Apr 5, 2024
4c270ec
touch event successful
adeledsg Apr 8, 2024
94f4158
Merge pull request #3 from Birkbeck2/touch-event
adeledsg Apr 8, 2024
1ebe3b1
Programmed the alert messages and reset button, have not done the use…
adeledsg Apr 9, 2024
bda41fe
Merge pull request #4 from Birkbeck2/mistakes-and-finish
adeledsg Apr 9, 2024
4cbc09e
Successfully added all media queries for responsive puzzle
adeledsg Apr 9, 2024
29aeb39
Merge pull request #5 from Birkbeck2/mediaqueries
adeledsg Apr 9, 2024
0c833ce
change reset button behaviour, added leave button and index page with…
adeledsg Apr 9, 2024
0825285
Merge pull request #6 from Birkbeck2/buttons
adeledsg Apr 9, 2024
c50ab7e
working on allowing users to drag back their pieces if they are not h…
adeledsg Apr 10, 2024
449f3d7
still figuring out touch user correction but added second puzzle
adeledsg Apr 12, 2024
3c684ca
added 3rd puzzle folder
adeledsg Apr 13, 2024
0e5b2ad
figured out how to improve cliking/dragging issue, still working on p…
adeledsg Apr 14, 2024
b349ada
added third puzzle, done css, still figuring out user correction
adeledsg Apr 15, 2024
81901ca
Merge pull request #7 from Birkbeck2/user-correction
adeledsg Apr 15, 2024
592ba33
user is not allowed to add more than one images to a div, adjsuted cs…
adeledsg Apr 15, 2024
6a6b3d5
Merge pull request #8 from Birkbeck2/user-correction
adeledsg Apr 15, 2024
c9fd81a
added alt text, width, height, read me file
adeledsg Apr 19, 2024
8555ef7
Merge pull request #9 from Birkbeck2/closing
adeledsg Apr 20, 2024
77f8eec
Update style.css
adeledsg Apr 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-24ddc0f5d75046c5622901739e7c5dd533143b0c8e959d652212380cedb1ea36.svg)](https://classroom.github.com/a/poMAp5Go)

This repository contains all the files necessary for a online Jigsaw Puzzle game. The website can be found at:

https://birkbeck2.github.io/web-project-adeledsg/index.html

This online game was made in the context of a homework assignment and is not for commercial use. It is submitted along a report via Turnitin.

# File content
The following HTML and Javascript files are used to create the game:

| File | Content |
| --- | --- |
| `index.html` | Landing page |
| `puzzle1.html` | Game 1 - 2x2 Puzzle |
| `puzzle2.html` | Game 2 - 3x3 Puzzle |
| `puzzle3.html` | Game 3 - 4x4 Puzzle |
| `interactive1.js` | Javascript for Puzzle 1 |
| `interactive2.js` | Javascript for Puzzle 2 |
| `interactive13.js` | Javascript for Puzzle 3 |

Additionally, a global stylesheet entitled `style.css` is used to set styles for all four HTML pages above.

Detailed comments providing explanation on implementation details, the files' structure, and references are mainly included in interactive1.js and style.CSS. As interactive2.js and intercative3.js follow the exact same logic, no further comments have been provided.

All images (individual puzzle pieces) can be found in either the `puzzle1`, `puzzle2`, or `puzzle3` folder.
26 changes: 26 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8">
<title>Jigsaw Puzzle</title>
<link rel="stylesheet" href="style.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<body>
<header>

</header>
<main>
<section>
<h1>Pick a jigsaw to complete</h1>
<ul id = "buttons">
<li><a href="puzzle1.html" id="puzzle1" class="button">Puzzle 1 (Easy)</a></li>
<li><a href="puzzle2.html" id="puzzle2" class="button">Puzzle 2 (Medium)</a></li>
<li><a href="puzzle3.html" id="puzzle3" class="button">Puzzle 3 (Hard)</a></li>
</ul>
</section>
</main>
</body>
</html>
226 changes: 226 additions & 0 deletions interactive1.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// I. Dynamically creating 4 div (box for the pieces) within my Puzzle container and then the img element within each div.
let puzzleContainer = document.getElementById("puzzle-container");

function creatingGrid() { //2*2 puzzle
let rows = 2;
let columns = 2;

for(let i=0; i<rows*columns; i++){ //creating 4divs and 4img
let pieceDiv = document.createElement('div') // creating div element
pieceDiv.className = "piece"; //class name added
puzzleContainer.appendChild(pieceDiv); //Nesting them in puzzlecontainer.
let img = document.createElement("img"); //creating img element
img.className = "images"; //class name added
img.id = `img${i}`; //unique ids
img.setAttribute("draggable", "true"); // Attribute required for events below, source: https://medium.com/@tatismolin/how-to-implement-drag-and-drop-functionality-using-vanilla-javascript-9ddfe2402695
pieceDiv.appendChild(img); //dynamically created 4 with individual id elements and nested them in the divs just created above.
}
}
creatingGrid(); //calling function

let img0 = document.getElementById("img0"); //Retrieving img elements just created
img0.src = "puzzle1/piece1.png"; //adding the source for the img elements.
img0.width = "200"; // Specifying width -space when browser loading the page
img0.height = "232"; //Specifying height -space when browser loading the page
img0.alt = "puzzle piece 1" //Specifying al text - accessibility

let img1 = document.getElementById("img1"); //Same as above, done 4 times
img1.src = "puzzle1/piece2.png";
img1.width = "232";
img1.height = "200";
img1.alt = "puzzle piece 2"

let img2 = document.getElementById("img2");
img2.src = "puzzle1/piece3.png";
img2.width = "232";
img2.height = "200";
img2.alt = "puzzle piece 3"

let img3 = document.getElementById("img3");
img3.src = "puzzle1/piece4.png";
img3.width = "199";
img3.height = "232";
img3.alt = "puzzle piece 4"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is good for a simple puzzle, but as you say in the report, it would be good to make the code DRYer (referring to the principle of "don't repeat yourself"). Maybe the best way to do that is to store the data needed to construct the puzzle in a bit of JSON, and then iterate over it to populate the HTML.

By the way, I do like the style of short lines and good descriptive variables, as it is nice and readable.


//II. Adding event listeners to start and reset buttons (Game Management)
let pieceContainer = document.getElementById("piece-container");

document.addEventListener('DOMContentLoaded', function () { // Assuring page is loaded
let startButton = document.getElementById('start');
let resetButton = document.getElementById('reset');

// Event listener for the start button

startButton.addEventListener('click', function(e){
startButton.style.display = 'none'; // Hiding the start button
resetButton.style.display = 'flex'; // Showing the reset button

Array.from(dropZone).forEach(function(zone){ //Iterating through elements with this class name as DOM returns the elements as an array
zone.style.border = "1px solid black"; //adding borders to div to indicate users where to put the pieces
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great use of Array.from and forEach


let pieces = document.getElementsByClassName("piece");
let element = e.target; //moving pieces (div which include their nested img) currently in Puzzle container to Piece container on click event for start button.
e.preventDefault();

Array.from(pieces).forEach(function(piece){ //Iterating through my piece divs (DOM understand class name as an array of all the element with that class)
let leftPosition = Math.floor(Math.random()*50); //giving them random position (iterated through numbers for the multiplier to make sure that the piece where relatively contained within the piece container.
let topPosition = Math.floor(Math.random()*50);
piece.style.position = "absolute";
piece.style.left = `${leftPosition}%`;
piece.style.top = `${topPosition}%`
pieceContainer.appendChild(piece);
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea!

});

// Event listener for the reset button
resetButton.addEventListener('click', function(e) {
resetButton.style.display = 'none'; // Hiding the reset button
startButton.style.display = 'flex'; // Showing the start button
let element = e.target;
window.location.reload(); // https://www.freecodecamp.org/news/javascript-refresh-page-how-to-reload-a-page-in-js/#:~:text=The%20simplest%20way%20to%20refresh,and%20loading%20the%20latest%20content.
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the start and reset buttons--they definitely put the user in control of the game and feel nice and intuitive.

});


//III. After adding the event, I realised that I needed to create new divs that will remain in the puzzle container so I can later drop the divs that have the nested img in them.
function creatingDropZones() { //2*2 puzzle - same principle as creatingGrid()
let rows = 2;
let columns = 2;

for(let i=0; i<rows*columns; i++){
let dropZone = document.createElement('div')
dropZone.className = "drop-zone";
puzzleContainer.appendChild(dropZone);
}
}
creatingDropZones();


//IV. Implementing logic of the game with drag and drop events:

/* A. Creating unique ID dynamically to my drag and drop divs, to later be able to create a function to match the correct place of each piece*/
let dropZone = document.getElementsByClassName("drop-zone");

Array.from(dropZone).forEach(function(dropZone, i){
dropZone.id = `drop-${i}`;
});

let pieceDiv = document.getElementsByClassName("piece");

Array.from(pieceDiv).forEach(function(pieceDiv, i){
pieceDiv.id = `drag-${i}`;
});

//B. Adding drag and drop events to the divs. Source: https://www.youtube.com/watch?v=_G8G1OrEOrI&ab_channel=DarwinTech

/*Process explained:
After implementing drag and drop, I realised that it does not work for mobile devices as they are mouse based events.
Instead I need to use touch start, move and end. As such, to make my code as clear as possible, I have created 3 functions below.
Since event listeners can take 2 parameters, each take either drag/drop/move or touch event as a firt parameter along with calling the appropriate function that will handle their common logic and account for their differences.*/
let draggableImages = document.getElementsByClassName("images");
var globalDraggedItemId = null; // set data and get data are methods that do not exist for touch events. Creating global variable to maintain state.

function handleStart(e) {
e.stopPropagation(); // ensure that the click event is just on the image not its parent
if (e.type === 'dragstart') {
console.log(e);
e.dataTransfer.setData('text/plain', e.currentTarget.id); //Seeting the data to add id to my target element that is my div and retriving it in drop- source: https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setData
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent. This is complicated stuff!

}
if (e.type === 'touchstart') {
e.preventDefault();
}
globalDraggedItemId = e.currentTarget.id; // setting data, source https://stackoverflow.com/questions/53530511/imitating-drag-and-drop-events-with-touch-events-for-mobile-devices
}

function handleOverMove(e){
e.preventDefault();
e.stopPropagation();
let draggedImage = e.currentTarget;
draggedImage.style.zIndex = 1000; // Without giving high index to the images and low to their divs, clicking on images didnt work if a div was above it.
let originalContainer = draggedImage.parentElement;
originalContainer.style.zIndex = 1;
}

function handleDropEnd(e) {
e.preventDefault();
e.stopPropagation();

let draggedItem;
let targetContainer = e.currentTarget;

if (targetContainer.querySelector('img')){ //if my dropdiv (target container) has an image already, user cant put another there.

} else {
if (e.type === 'drop') {
let data = e.dataTransfer.getData("text/plain"); //retrieving id and moving it the div to target.
draggedItem = document.getElementById(data);
} else if (e.type === 'touchend') {
draggedItem = document.getElementById(globalDraggedItemId);
}
if (draggedItem) {
e.currentTarget.appendChild(draggedItem);
globalDraggedItemId = null; // Reset the global variable
}

checkPosition(); //calling function for alert messages below
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is really impressive that you have handled both click-and-drag for mouse users and double-touch usability for touch screens. I even found the UX better on my phone than with a mouse.


}

}


Array.from(draggableImages).forEach(function(image){ //Iterating through each element and assigning touch and mouse event to each.
image.addEventListener('dragstart', handleStart);
image.addEventListener('touchstart', handleStart);
});

Array.from(dropZone).forEach(function(zone){
zone.addEventListener('dragover', handleOverMove);
zone.addEventListener('touchmove', handleOverMove);
zone.addEventListener('drop', handleDropEnd);
zone.addEventListener('touchend', handleDropEnd);
});

// V. Finish message
let correctPosition = { // creating an object using individual ids created earlier.
img0: "drop-0",
img1: "drop-1",
img2: "drop-2",
img3: "drop-3",
};

function checkPosition() {
let allPlaced = true;
let allCorrect = true;

for (let [imgId, dropId] of Object.entries(correctPosition)) { //iterating through my object that contains the correct positions of each piece.
let image = document.getElementById(imgId);
let currentDrop = image.parentElement.id;

// Checking if all pieces are placed in the drop zone divs
if (!image.parentElement || !image.parentElement.classList.contains('drop-zone')) {
allPlaced = false;
}

// Checking if the images' parents' id are that of the dropzone id => aka they are they correctly placed
if (currentDrop !== dropId) {
allCorrect = false;
}
}

if (allPlaced && !allCorrect) {// All pieces are placed, but some are wrong
setTimeout(() => {
alert("You are almost there! But some pieces are in the wrong place.");
}, 500); // Delay to allow for the puzzle to be visually completed before showing the message
} else if (allPlaced && allCorrect) { // All pieces are placed correctly
Array.from(dropZone).forEach(function(drop){ //purely aesthetics considerations, otherwise, we would see the borders
drop.style.border = "none";
})
setTimeout(() => {
alert("Congratulations! You completed the puzzle.");
}, 500);
}

return true;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a complex function! Bravo for tackling the logic here.

Loading