Getting a position of an element in a 2D array - javascript

So I'm building a a turn based board game which needs to contain 2 player that can move across the map. I'm stuck at getting the position of the player(which is just a simple div element) inside of that 2D array. I've tried using indexOf, but even tho it's placed inside an onclick function, always returns 0.
The html code contains just of few div's with col classes:
And here is the JavaScript code (btw it contains some unnecessary stuff that I've just added for test purposes) :
let row = document.querySelector('.row');
let fields = document.getElementsByClassName('col-md-2')
let fieldsArr = Array.from(fields);
let header = document.getElementById("clicked");
let cols = header.getElementsByClassName("col-md-2");
let player = document.getElementById('player');
let player2 = document.getElementById('player2');
let blockedField = document.getElementsByClassName('blocked');
fieldsArr.sort(function() {
return 0.5 - Math.random();
}).forEach(function(el) {
row.appendChild(el);
});
// ADD AN EVENT LISTENER AND LISTEN FOR COLS ID
function replyClick(e) {
e = e || window.event;
e = e.target || e.srcElement;
if (e.nodeName === 'DIV') {
let changable = e.id;
//console.log(changable);
}
}
// CREATE A 2D ARRAY (MAP)
var map = [];
while (fieldsArr.length) map.push(fieldsArr.splice(0, 6));
// ON CLICK ADD A CLASS OF ACTIVE
for (var i = 0; i < cols.length; i++) {
cols[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
// MOVE PLAYER ONE ACROSS THE MAP
function movePlayer(multWidth, multHeight) {
$(".active").append(player);
if ((row).click > multWidth) {
alert(1)
}
}
// MOVE PLAYER 2 ACROSS THE MAP
function movePlayer2() {
$(".active").append(player2);
}
// MAKE GRAYED OUT FIELD UNAVALIABLE AND SHOW AN ALERT
$(blockedField).css("pointer-events", "none");
// APPEND PLAYER1(2) TO THE FIRST(LAST) FIELD ON THE MAP
map[0][0].appendChild(player);
map[5][5].appendChild(player2);
// GET PLAYERS CURRENT POSITION
$(row).click(function() {
let current = player.offsetTop;
});
const widthAllowed = 3 * 156;
const heightAllowed = 3 * 146;
// LIMIT PLAYER MOVES
let player1Moves = 3;
player2Moves = 3;
$(row).click(function() {
movePlayer();
let remainingMoves = player1Moves -= 1;
if (remainingMoves === 0) {
alert("You don't have any more moves. Player's 2 turn.");
$(player).css("pointer-events", "none");
$(row).click(movePlayer2);
}
})
for (var x = 0; x < map.length; x++) {
for (var y = 0; y < map[x].length; y++) {
console.log(x, y);
}
}
console.log(map);
console.log(map[2][5]);
console.log(map[5][0]);

You should take some beginner jquery/javascript course, since your question is very simple and you will find the whole programming thing way easier with a few basic concepts (like selectors, events and callbacks)
That said, here is a basic example of how to return the div element that contains the player element and how to use event attachment instead of inline events.
let row = $('.row');
row.on('click', replyClick);
function replyClick(e) {
var targetRow = $(e.target);
$('.row > div.active').removeClass('active');
targetRow.addClass('active');
var player = $('.row div.player');
alert(player.parent().attr('id'));
};
.player {
width: 20px;
height: 20px;
background: red;
}
.row > div {
padding: 10px;
width: 20px;
height: 20px;
border: 1px solid red;
}
.row > div.active {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div id="col1" class="col-md-2">
<div class="player"></div>
</div>
<div id="col2" class="col-md-2 blocked"></div>
<div id="col3" class="col-md-2 active"></div>
<div id="col4" class="col-md-2"></div>
</div>
</div>

Related

Drag and drop returns 'null' on item created by DOM

I'm currently working on a trello-like app, and have run into some issues with drag and drop events, and DOM. I basically have four lists, each containing a div with id "card-container". I'm able to move cards created in the source code as expected, however, when I add a card through DOM, and try to move it to another container, the container appends a child "null". Not sure what I am doing wrong.
When adding a new card, the function makeCard() is run by submitting a button.
draganddorp.js
const card = document.querySelector('.task-card');
const cards = document.querySelectorAll('.task-card')
const cardContainers = document.querySelectorAll('.card-container');
var draggingCard = null;
// card listeners
cards.forEach(addCardListeners);
// Loop through taskContainer boxes and add listeners
cardContainers.forEach(addContainerListeners);
// Drag Functions
function dragStart(event) {
this.className += ' card-hold';
setTimeout(() => (this.className = 'invisible'), 0); //set timeout so card wont dissapear
draggingCard = event.target;
}
function dragEnd() {
this.className = 'task-card';
draggingCard = null;
}
function dragOver(e) {
e.preventDefault();
}
function dragEnter(e) {
e.preventDefault();
this.className += ' card-container-hover';
}
function dragLeave() {
this.className = 'card-container';
}
function dragDrop() {
this.className = 'card-container';
this.append(draggingCard);
}
function addCardListeners(card) {
card.addEventListener('dragstart', dragStart);
card.addEventListener('dragend', dragEnd);
}
function addContainerListeners(cardContainer) {
cardContainer.addEventListener('dragover', dragOver);
cardContainer.addEventListener('dragenter', dragEnter);
cardContainer.addEventListener('dragleave', dragLeave);
cardContainer.addEventListener('drop', dragDrop);
}
makecard.js
function makeCard(destination) {
//defining all variables needed for creating a card
let getCardContainer = document.getElementById(destination);
let createTaskCard = document.createElement("div");
//varibles needed for task header
let createTaskHeader = document.createElement("div");
let createTags = document.createElement("div");
let createTag = document.createElement("span");
let createActionsBtn = document.createElement("div");
//varibles needed for task body
let createTaskBody = document.createElement("div");
let createTaskTitle = document.createElement("p");
//varibles needed for task footer
let createTaskFooter = document.createElement("div");
let createAsignee = document.createElement("div");
let createAsigneeIcon = document.createElement("span");
let createAsigneeMember = document.createElement("span");
let createDueDate = document.createElement("div");
let createDueDateDate = document.createElement("span");
let createDueDateIcon = document.createElement("span");
//creating card
createTaskCard.className = "task-card";
createTaskCard.setAttribute("draggable", true);
//addding class/id and HTML to task header
createTaskHeader.className = "task-card-header";
createTags.className = "tags";
createTag.className = "tag";
createTag.id = "tag-";
createTag.innerHTML = "someTags"
createActionsBtn.className = "actions";
//add action itself (svg)
//addding class/id and HTML to task body
createTaskBody.className = "task-card-body";
createTaskTitle.innerHTML = "someTitle"
//addding class/id and HTML to task footer
createTaskFooter.className = "task-card-footer";
createAsignee.className = "asignee";
createAsigneeIcon.className = "icon";
createAsigneeIcon.innerHTML = "I";
createAsigneeMember.innerHTML = "Assignee name";
createDueDate.className = "dueDate";
createDueDateDate.innerHTML = "someDate"
createDueDateIcon.className = "icon";
createDueDateIcon.innerHTML = "I";
//setting up structure
createTaskHeader.appendChild(createTags);
createTaskCard.appendChild(createTaskHeader);
createTags.appendChild(createTag);
createTaskHeader.appendChild(createActionsBtn);
createTaskCard.appendChild(createTaskBody);
createTaskBody.appendChild(createTaskTitle);
createTaskCard.appendChild(createTaskFooter);
createTaskFooter.appendChild(createAsignee);
createAsignee.appendChild(createAsigneeIcon);
createTaskFooter.appendChild(createDueDate);
createAsignee.appendChild(createAsigneeMember)
createDueDate.appendChild(createDueDateDate);
createDueDate.appendChild(createDueDateIcon);
//appending card to card container
getCardContainer.appendChild(createTaskCard);
}
html
<div class="task-card" draggable="true">
<div class="task-card-header">
<div class="tags">
<span class="tag">Priority</span>
<span class="tag">Design</span>
</div>
<div class="actions">
<a href="#">
<!--icon-->
</a>
</div>
</div>
<div class="task-card-body">
<p>Test</p>
</div>
<div class="task-card-footer">
<div class="asignee">
<span class="icon">
<!--icon-->
</span>
<span>Daniel Kjellid</span>
</div>
<div class="dueDate">
<span>23.05</span>
<span class="icon">
<!--icon-->
</span>
</div>
</div>
</div>
css
.card-container {
background: white;
height: auto;
margin: 2px;
min-height: 150px;
width: 115%;
}
.card-container-hover {
border: dashed 3px #F364A2 !important;
}
.card-dragging {
display: absolute;
}
.card-hold {
border: solid 5px #ccc;
}
.task-card {
border-radius: 8px;
box-shadow: 0 0 4px 0 rgba(0,0,0,0.50);
color: #3E4C59;
height: auto;
margin-bottom: 13px;
width: 100%;
}
It seems to me when you make a new card in the makeCard(destination) function, you do not add the dragstart snd dragend listeners to it. As such draggingCard is still null because it has not been set (which happens in the dragStart event listener).
Try adding this to the end of your makeCard function.
addCardListeners(createTaskCard);
Ofcourse, it is a bit hard to help without a working example.

Javascript Can Push() and Pop() and Image Replacement Work within an Array

Can Push() and Pop() and Image Replacement Work within an Array?
8th Gr math teacher attempting to create a slide show of question images that pop() and push() through an image array based on student responses. If the student answers correctly the question is popped, but if they answer incorrectly it is added to the end of the queue. Additionally, since deleting elements in the DOM is bad, I am replacing the current image's src and id with that of the next element in queue. The array is then popped and pushed along, but whenever I enter in the incorrect answer twice the same image appears.
I have moved the global variable that holds the array, domEls, inside of the function retrieveAnsForImage to force it to randomize the images in the array. When I do this, the images change correctly so I believe it is the push() and pop() commands.
I included a snippet that doesn't work here, but works like a champ in Notepad ++. I just took a crash course in Javascript, HTML and CSS last month on Codecademy, I am very new to this. Thank you for reading.
//Jquery
$(document).ready(function() {
$(function() {
$('img.card').on('contextmenu', function(e) {
e.preventDefault();
//alert(this.id);
openPrompt(this.id);
});
});
});
//Provide and Shuffle array function
function shuffleImgs() {
var imgArr = [
"image1",
"image2",
"image3",
"image4",
"image5",
"image6",
"image7",
"image8",
"image9"
];
var currentIndex = imgArr.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = imgArr[currentIndex];
imgArr[currentIndex] = imgArr[randomIndex];
imgArr[randomIndex] = temporaryValue;
}
alert("shuffle");
return imgArr;
}
function arrStack() {
var imgArr = shuffleImgs();
//Map over the array to create Dom elements
var domElements = imgArr.map(function (imgName, index) {
var cardDiv = document.createElement('div');
var cardImage = document.createElement('img');
//Add img id and class
cardImage.id = imgName;
cardImage.classList.add('card');
//Set img source
cardImage.src = `images/${imgName}.jpg`;
//Put it all together
cardDiv.appendChild(cardImage);
return cardDiv;
});
//this notation to call nested function for Global var stack
this.nDomElements = function () {
stackDomEl = domElements;
return stackDomEl;
}
//Display last element in array
//this notation to call the nested function from outside the function
this.nDisplayLastArr = function displayLastArr() {
var lastImgArr = domElements[domElements.length - 1];
//alert(lastImgArr);
//Append the elements to the DOM
var modal = document.querySelector('div.modal');
modal.appendChild(lastImgArr);
return lastImgArr; //Use brackets when your are returning more than one variable
}
}
//Function called from Jquery to open prompt to answer question
function openPrompt(imageId) {
var userAns = prompt("Please enter your answer below and click OK");
if (userAns == null || userAns == "") {
alert("User cancelled the prompt. Exit and please try again!");
}
else {
/*Vain hope that I can pass imageId from click event through the user prompt
to the answer checking function retrieveAnsForImage*/
retrieveAnsForImage(imageId, userAns); //out of scope?
}
}
//Global variable
func = new arrStack();
window.domEls = func.nDomElements();
//Compare user responses with the question image by use of the click image id
function retrieveAnsForImage(imageId, userAns) {
//Change these variables to the correct answer whenever this website is reused in other assignments
var ansImage1 = "1";
var ansImage2 = "2";
var ansImage3 = "3";
var ansImage4 = "4";
var ansImage5 = "5";
var ansImage6 = "6";
var ansImage7 = "7";
var ansImage8 = "8";
var ansImage9 = "9";
//Give students a second chance to retry a question
//var hintCounter = 0; //include a while statement above the if statements to allow students a retry
/*Compare user response with correct case answer and correct clicked image.
Students may enter the right answer for the wrong image hence the &&.
Images will always be refered to as image1, image2, etc.*/
if (userAns === ansImage1 && imageId === "image1") {
correctAns(imageId);
}
else if (userAns === ansImage2 && imageId === "image2") {
correctAns(imageId);
}
else if (userAns === ansImage3 && imageId === "image3") {
correctAns(imageId);
}
else if (userAns === ansImage4 && imageId === "image4") {
correctAns(imageId);
}
else if (userAns === ansImage5 && imageId === "image5") {
correctAns(imageId);
}
else if (userAns === ansImage6 && imageId === "image6") {
correctAns(imageId);
}
else if (userAns === ansImage7 && imageId === "image7") {
correctAns(imageId);
}
else if (userAns === ansImage8 && imageId === "image8") {
correctAns(imageId);
}
else if (userAns === ansImage9 && imageId === "image9") {
correctAns(imageId);
}
else {
window.alert("Incorrect Answer");
incorrectAns();
}
function correctAns(){
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change im
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
domEls.pop();
//Think about when the array is completely gone
//while domEls.length !== 0;
}
function incorrectAns(){
//Last element in array
var LastElArr = domEls[domEls.length - 1];
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change image src and id to next element in queue
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
//Remove last element in array
domEls.pop();
//move the last element to the first element in the array for another attempt
domEls.push(LastElArr);
alert(domEls.length);
}
}
function overlay() {
var el = document.getElementById("overlay");
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible";
}
#overlay {
visibility: hidden;
position: absolute;
left: 0px;
top: 0px;
width:100%;
height:100%;
text-align:center;
z-index: 1000;
background-color: rgba(0,191, 255, 0.8);
}
#overlay div {
width:70%;
margin: 10% auto;
background-color: #fff;
border:1px solid #000;
padding:15px;
text-align: center;
}
body {
height:100%;
margin:0;
padding:0;
}
#close-img {
float: right;
clear: right;
width: 30px;
height: 30px;
bottom: 0;
right: 0;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<span> "Left click to view any questions. Right click (two finger tap) to answer the question and claim the tile. Each player must claim 4 tiles to successfully complete the assignment."</span>
<link href="https://fonts.googleapis.com/css?family=Oswald:300,700|Varela+Round" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="Stack Rnd Temp.css">-->
<script type="text/javascript" src="Stack Rnd Temp.js"></script>
<script src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="StackRndTempjq.js"></script>
</head>
<body>
<div class="title">
<h1></h1>
</div>
<div id="gameboard"> <!--Container for all nine divs-->
<a href='#' onclick='overlay()'>Click here to show the overlay</a>
</div>
<div class="modal" id="overlay">
<p> "Right click to answer the question"</p>
<script>
func = new arrStack();
func.nDisplayLastArr();
</script>
<img src="images/close.png" id="close-img" onclick="overlay()">
</div>
</body>
</html>
Your issue is that pop removes the last element from the array while push adds the element to end of the array.
What you probably want to do is use shift to remove the the first element from the array and pop it back to the end if the answer is wrong.
Alternately, you could pop the last element and use unshift to insert back into the beginning of you want to work in the other direction.
Here's a quick mockup without images.
var currentTest = null;
function getTest() {
$('#answer').html("").hide();
if (tests.length > 0) {
currentTest = tests.shift(); // remove the first question
$('#question').fadeIn(450).html(currentTest.q);
return currentTest;
} else {
$('#answer').html("Finished").fadeIn(500);
$('#btnCorrect').unbind();
$('#btnWrong').unbind();
}
}
var tests = [];
for (var i = 0; i < 5; i++) {
var question = "Question " + i;
var answer = "Answer " + i;
tests.push({
q: question,
a: answer
});
}
$('#btnCorrect').click(function() {
$('#question').hide();
$('#answer').fadeIn(450).html("Correct!");
window.setTimeout(getTest, 750);
});
$('#btnWrong').click(function() {
$('#question').hide();
tests.push(currentTest); // put the question back in the array
$('#answer').fadeIn(450).html("Incorrect!");
window.setTimeout(getTest, 750);
});
$(document).ready(function() {
getTest();
})
* {
font-family: arial;
}
#panel {
height: 50px;
}
#answer {
border: 1px solid #cccccc;
background: #dedede;
width: 400px;
}
#question {
border: 1px solid #999999;
background: #dedede;
width: 400px;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="panel">
<div id="answer"></div>
<div id="question"></div>
</div>
<input id="btnCorrect" value="Mock Correct Answer" type="button">
<input id="btnWrong" value="Mock Wrong Answer" type="button">
</body>
</html>

Checking function for sliding puzzle javascript

I created a sliding puzzle with different formats like: 3x3, 3x4, 4x3 and 4x4. When you run my code you can see on the right side a selection box where you can choose the 4 formats. The slidingpuzzle is almost done. But I need a function which checks after every move if the puzzle is solved and if that is the case it should give out a line like "Congrantulations you solved it!" or "You won!". Any idea how to make that work?
In the javascript code you can see the first function loadFunc() is to replace every piece with the blank one and the functions after that are to select a format and change the format into it. The function Shiftpuzzlepieces makes it so that you can move each piece into the blank space. Function shuffle randomizes every pieces position. If you have any more question or understanding issues just feel free to ask in the comments. Many thanks in advance.
Since I don't have enough reputation I will post a link to the images here: http://imgur.com/a/2nMlt . These images are just placeholders right now.
Here is the jsfiddle:
http://jsfiddle.net/Cuttingtheaces/vkyxgwo6/19/
As always, there is a "hacky", easy way to do this, and then there is more elegant but one that requires significant changes to your code.
Hacky way
To accomplish this as fast and dirty as possible, I would go with parsing id-s of pieces to check if they are in correct order, because they have this handy pattern "position" + it's expected index or "blank":
function isFinished() {
var puzzleEl = document.getElementById('slidingpuzzleContainer').children[0];
// convert a live list of child elements into regular array
var pieces = [].slice.call(puzzleEl.children);
return pieces
.map(function (piece) {
return piece.id.substr(8); // strip "position" prefix
})
.every(function (id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == parseInt(id);
});
}
Now we need to check it somewhere, and naturally the best place would be after each move, so shiftPuzzlepieces() should be updated to call isFinished() function, and show the finishing message if it returns true:
function shiftPuzzlepieces(el) {
// ...
if (isFinished()) {
alert("You won!");
}
}
And voilĂ : live version.
How would I implement this game
For me, the proper way of implementing this would be to track current positions of pieces in some data structure and check it in similar way, but without traversing DOM or checking node's id-s. Also, it would allow to implement something like React.js application: onclick handler would mutate current game's state and then just render it into the DOM.
Here how I would implement the game:
/**
* Provides an initial state of the game
* with default size 4x4
*/
function initialState() {
return {
x: 4,
y: 4,
started: false,
finished: false
};
}
/**
* Inits a game
*/
function initGame() {
var gameContainer = document.querySelector("#slidingpuzzleContainer");
var gameState = initialState();
initFormatControl(gameContainer, gameState);
initGameControls(gameContainer, gameState);
// kick-off rendering
render(gameContainer, gameState);
}
/**
* Handles clicks on the container element
*/
function initGameControls(gameContainer, gameState) {
gameContainer.addEventListener("click", function hanldeClick(event) {
if (!gameState.started || gameState.finished) {
// game didn't started yet or already finished, ignore clicks
return;
}
if (event.target.className.indexOf("piece") == -1) {
// click somewhere not on the piece (like, margins between them)
return;
}
// try to move piece somewhere
movePiece(gameState, parseInt(event.target.dataset.index));
// check if we're done here
checkFinish(gameState);
// render the state of game
render(gameContainer, gameState);
event.stopPropagation();
return false;
});
}
/**
* Checks whether game is finished
*/
function checkFinish(gameState) {
gameState.finished = gameState.pieces.every(function(id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == id;
});
}
/**
* Moves target piece around if there's blank somewhere near it
*/
function movePiece(gameState, targetIndex) {
if (isBlank(targetIndex)) {
// ignore clicks on the "blank" piece
return;
}
var blankPiece = findBlankAround();
if (blankPiece == null) {
// nowhere to go :(
return;
}
swap(targetIndex, blankPiece);
function findBlankAround() {
var up = targetIndex - gameState.x;
if (targetIndex >= gameState.x && isBlank(up)) {
return up;
}
var down = targetIndex + gameState.x;
if (targetIndex < ((gameState.y - 1) * gameState.x) && isBlank(down)) {
return down;
}
var left = targetIndex - 1;
if ((targetIndex % gameState.x) > 0 && isBlank(left)) {
return left;
}
var right = targetIndex + 1;
if ((targetIndex % gameState.x) < (gameState.x - 1) && isBlank(right)) {
return right;
}
}
function isBlank(index) {
return gameState.pieces[index] == "blank";
}
function swap(i1, i2) {
var t = gameState.pieces[i1];
gameState.pieces[i1] = gameState.pieces[i2];
gameState.pieces[i2] = t;
}
}
/**
* Handles form for selecting and starting the game
*/
function initFormatControl(gameContainer, state) {
var formatContainer = document.querySelector("#formatContainer");
var formatSelect = formatContainer.querySelector("select");
var formatApply = formatContainer.querySelector("button");
formatSelect.addEventListener("change", function(event) {
formatApply.disabled = false;
});
formatContainer.addEventListener("submit", function(event) {
var rawValue = event.target.format.value;
var value = rawValue.split("x");
// update state
state.x = parseInt(value[0], 10);
state.y = parseInt(value[1], 10);
state.started = true;
state.pieces = generatePuzzle(state.x * state.y);
// render game
render(gameContainer, state);
event.preventDefault();
return false;
});
}
/**
* Renders game's state into container element
*/
function render(container, state) {
var numberOfPieces = state.x * state.y;
updateClass(container, state.x, state.y);
clear(container);
var containerHTML = "";
if (!state.started) {
for (var i = 0; i < numberOfPieces; i++) {
containerHTML += renderPiece("", i) + "\n";
}
} else if (state.finished) {
containerHTML = "<div class='congratulation'><h2 >You won!</h2><p>Press 'Play!' to start again.</p></div>";
} else {
containerHTML = state.pieces.map(renderPiece).join("\n");
}
container.innerHTML = containerHTML;
function renderPiece(id, index) {
return "<div class='piece' data-index='" + index + "'>" + id + "</div>";
}
function updateClass(container, x, y) {
container.className = "slidingpuzzleContainer" + x + "x" + y;
}
function clear(container) {
container.innerHTML = "";
}
}
/**
* Generates a shuffled array of id-s ready to be rendered
*/
function generatePuzzle(n) {
var pieces = ["blank"];
for (var i = 0; i < n - 1; i++) {
pieces.push(i);
}
return shuffleArray(pieces);
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
}
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
font-size: 12px;
color: #000;
}
#formatContainer {
position: absolute;
top: 50px;
left: 500px;
}
#formatContainer label {
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
}
#formatContainer select {
display: block;
width: 100%;
margin-top: 10px;
margin-bottom: 10px;
}
#formatContainer button {
display: inline-block;
width: 100%;
}
.piece {
width: 96px;
height: 96px;
margin: 1px;
float: left;
border: 1px solid black;
}
.slidingpuzzleContainer3x3,
.slidingpuzzleContainer3x4,
.slidingpuzzleContainer4x3,
.slidingpuzzleContainer4x4 {
position: absolute;
top: 50px;
left: 50px;
border: 10px solid black;
}
.slidingpuzzleContainer3x3 {
width: 300px;
height: 300px;
}
.slidingpuzzleContainer3x4 {
width: 300px;
height: 400px;
}
.slidingpuzzleContainer4x3 {
width: 400px;
height: 300px;
}
.slidingpuzzleContainer4x4 {
width: 400px;
height: 400px;
}
.congratulation {
margin: 10px;
}
}
<body onload="initGame();">
<div id="slidingpuzzleContainer"></div>
<form id="formatContainer">
<label for="format">select format:</label>
<select name="format" id="format" size="1">
<option value="" selected="true" disabled="true"></option>
<option value="3x3">Format 3 x 3</option>
<option value="3x4">Format 3 x 4</option>
<option value="4x3">Format 4 x 3</option>
<option value="4x4">Format 4 x 4</option>
</select>
<button type="submit" disabled="true">Play!</button>
</form>
</body>
Here we have the initGame() function that starts everything. When called it will create an initial state of the game (we have default size and state properties to care about there), add listeners on the controls and call render() function with the current state.
initGameControls() sets up a listener for clicks on the field that will 1) call movePiece() which will try to move clicked piece on the blank spot if the former is somewhere around, 2) check if after move game is finished with checkFinish(), 3) call render() with updated state.
Now render() is a pretty simple function: it just gets the state and updates the DOM on the page accordingly.
Utility function initFormatControl() handles clicks and updates on the form for field size selection, and when the 'Play!' button is pressed will generate initial order of the pieces on the field and call render() with new state.
The main benefit of this approach is that almost all functions are decoupled from one another: you can tweak logic for finding blank space around target piece, to allow, for example, to swap pieces with adjacent ids, and even then functions for rendering, initialization and click handling will stay the same.
$(document).on('click','.puzzlepiece', function(){
var count = 0;
var imgarray = [];
var test =[0,1,2,3,4,5,6,7,8,'blank']
$('#slidingpuzzleContainer img').each(function(i){
var imgalt = $(this).attr('alt');
imgarray[i] = imgalt;
count++;
});
var is_same = (imgarray.length == test.length) && imgarray.every(function(element, index) {
return element === array2[index];
});
console.log(is_same); ///it will true if two array is same
});
try this... this is for only 3*3.. you pass the parameter and makethe array value as dynamically..

Javascript - next/previous buttons on slide show - New kid needs assistance

I am brand new at this so I apologize because I'm sure an intermediate could pull his or her answer from what's already been asked, but I need specific help.
I'm having trouble getting my "next" and "previous" buttons for my slideshow to work in Javascript. Once the user clicks through all 5 images, it needs to return to the first image, ready to click through again-- a continuous loop. I think arrays are supposed to be utilized. What am I missing?
Thank you!!
var imageCache = [];
var imageItem = 0;
var images = 0;
var captionNode;
var imageNode;
var $ = function (id) {
return document.getElementById(id);
}
window.onload = function () {
var listNode = $("image_list");
var nextButton = $("next");
var previousButton = $("previous");
captionNode = $("caption");
imageNode = $("image");
var links = listNode.getElementsByTagName("a");
var i, linkNode, image;
for ( i = 0; i < links.length; i++ ) {
linkNode = links[i];
// Pre-load image and copy title properties.
image = new Image();
image.src = linkNode.getAttribute("href");
image.title = linkNode.getAttribute("title");
imageCache.push(image);
}
// Now record the total images we have.
images = imageCache.length;
// Set up the button handlers.
nextButton.onclick = nextButtonClick;
previousButton.onclick = previousButtonClick;
}
function nextButtonClick() {
}
function previousButtonClick() {
}
article, aside, figure, figcaption, footer, header, nav, section {
display: block;
}
body {
font-family: Arial, Helvetica, sans-serif;
width: 380px;
margin: 0 auto;
padding: 20px;
border: 3px solid blue;
}
h1, h2, ul, p {
margin: 0;
padding: 0;
}
h1 {
padding-bottom: .25em;
color: blue;
}
h2 {
font-size: 120%;
padding: .5em 0;
}
ul {
display: none;
}
img {
height: 250px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Slide Show</title>
<link rel="stylesheet" href="main.css">
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<script src="slide_show.js"></script>
</head>
<body>
<section>
<h1>Fishing Slide Show</h1>
<ul id="image_list">
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<h2 id="caption">Casting on the Upper Kings</h2>
<p>
<img src="images/casting1.jpg" alt="" id="image">
</p>
<input type="button" value="Previous" name="previous" id="previous">
<input type="button" value="Next" name="next" id="next">
</section>
</body>
</html>
You have the following variables:
var imageCache = [];
var imageItem = 0;
var images = 0;
Presumably imageItem is the index of the currently displayed image (e.g. 0 for the first one) and images is the number of images (i.e. imageCache.length). To get the next image:
imageItem = ++imageItem % images;
var nextImage = imageCache[imageItem];
This will wrap around to zero when imageItem reaches the number of images in the cache. Similarly for previous:
imageItem = (--imageItem + images) % images;
var prevImage = imageCache[imageItem];
so that when imageItem reaches 0, subtracting 1 goes to -1 and adding imageCache.length sets it to the last image. The rest of the time it's left at imageItem - 1.
It's up to you to fill in the rest of the code. :-)
I would use an array zipper to implement the next and prev functions. An array zipper is a data structure that allows you to move forward and backward through an array.
function ArrayZipper(array) {
var length = array.length, index = 0;
this.getCurrent = function () {
return array[index];
};
this.getNext = function () {
return array[index = (index + 1) % length];
};
this.getPrevious = function () {
return array[index = (length + index - 1) % length];
};
}
You can use an array zipper to create a slide show as follows:
var zipper = new ArrayZipper([ "black"
, "blue"
, "green"
, "cyan"
, "red"
, "magenta"
, "yellow"
, "white"
]);
var style = $("color").style;
style.backgroundColor = zipper.getCurrent();
$("next").addEventListener("click", function () {
style.backgroundColor = zipper.getNext();
});
$("prev").addEventListener("click", function () {
style.backgroundColor = zipper.getPrevious();
});
function $(id) {
return document.getElementById(id);
}
function ArrayZipper(array) {
var length = array.length, index = 0;
this.getCurrent = function () {
return array[index];
};
this.getNext = function () {
return array[index = (index + 1) % length];
};
this.getPrevious = function () {
return array[index = (length + index - 1) % length];
};
}
#color {
height: 100px;
width: 100px;
}
<div id="color"></div>
<button id="next">Next</button>
<button id="prev">Prev</button>
Hope that helps.

Drag and drop with javascript

I just tried a walkthrough for creating drag and drop-enabled features. I followed the guide at http://www.webreference.com/programming/javascript/mk/column2/index.html
Even though the guide is well written I cant get this to function, any obvious mistakes here? All of my code is pasted below and the guide authors comments are there as well to clarify. When I run the page displays all the items but nothing happens when I try moving them.
<%# Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript">
// iMouseDown represents the current mouse button state: up or down
/*
lMouseState represents the previous mouse button state so that we can
check for button clicks and button releases:
if(iMouseDown && !lMouseState) // button just clicked!
if(!iMouseDown && lMouseState) // button just released!
*/
var mouseOffset = null;
var iMouseDown = false;
var lMouseState = false;
var dragObject = null;
// Demo 0 variables
var DragDrops = [];
var curTarget = null;
var lastTarget = null;
var dragHelper = null;
var tempDiv = null;
var rootParent = null;
var rootSibling = null;
Number.prototype.NaN0 = function () { return isNaN(this) ? 0 : this; }
function CreateDragContainer() {
/*
Create a new "Container Instance" so that items from one "Set" can not
be dragged into items from another "Set"
*/
var cDrag = DragDrops.length;
DragDrops[cDrag] = [];
/*
Each item passed to this function should be a "container". Store each
of these items in our current container
*/
for (var i = 0; i < arguments.length; i++) {
var cObj = arguments[i];
DragDrops[cDrag].push(cObj);
cObj.setAttribute('DropObj', cDrag);
/*
Every top level item in these containers should be draggable. Do this
by setting the DragObj attribute on each item and then later checking
this attribute in the mouseMove function
*/
for (var j = 0; j < cObj.childNodes.length; j++) {
// Firefox puts in lots of #text nodes...skip these
if (cObj.childNodes[j].nodeName == '#text') continue;
cObj.childNodes[j].setAttribute('DragObj', cDrag);
}
}
}
function mouseMove(ev) {
ev = ev || window.event;
/*
We are setting target to whatever item the mouse is currently on
Firefox uses event.target here, MSIE uses event.srcElement
*/
var target = ev.target || ev.srcElement;
var mousePos = mouseCoords(ev);
// mouseOut event - fires if the item the mouse is on has changed
if (lastTarget && (target !== lastTarget)) {
// reset the classname for the target element
var origClass = lastTarget.getAttribute('origClass');
if (origClass) lastTarget.className = origClass;
}
/*
dragObj is the grouping our item is in (set from the createDragContainer function).
if the item is not in a grouping we ignore it since it can't be dragged with this
script.
*/
var dragObj = target.getAttribute('DragObj');
// if the mouse was moved over an element that is draggable
if (dragObj != null) {
// mouseOver event - Change the item's class if necessary
if (target != lastTarget) {
var oClass = target.getAttribute('overClass');
if (oClass) {
target.setAttribute('origClass', target.className);
target.className = oClass;
}
}
// if the user is just starting to drag the element
if (iMouseDown && !lMouseState) {
// mouseDown target
curTarget = target;
// Record the mouse x and y offset for the element
rootParent = curTarget.parentNode;
rootSibling = curTarget.nextSibling;
mouseOffset = getMouseOffset(target, ev);
// We remove anything that is in our dragHelper DIV so we can put a new item in it.
for (var i = 0; i < dragHelper.childNodes.length; i++) dragHelper.removeChild(dragHelper.childNodes[i]);
// Make a copy of the current item and put it in our drag helper.
dragHelper.appendChild(curTarget.cloneNode(true));
dragHelper.style.display = 'block';
// set the class on our helper DIV if necessary
var dragClass = curTarget.getAttribute('dragClass');
if (dragClass) {
dragHelper.firstChild.className = dragClass;
}
// disable dragging from our helper DIV (it's already being dragged)
dragHelper.firstChild.removeAttribute('DragObj');
/*
Record the current position of all drag/drop targets related
to the element. We do this here so that we do not have to do
it on the general mouse move event which fires when the mouse
moves even 1 pixel. If we don't do this here the script
would run much slower.
*/
var dragConts = DragDrops[dragObj];
/*
first record the width/height of our drag item. Then hide it since
it is going to (potentially) be moved out of its parent.
*/
curTarget.setAttribute('startWidth', parseInt(curTarget.offsetWidth));
curTarget.setAttribute('startHeight', parseInt(curTarget.offsetHeight));
curTarget.style.display = 'none';
// loop through each possible drop container
for (var i = 0; i < dragConts.length; i++) {
with (dragConts[i]) {
var pos = getPosition(dragConts[i]);
/*
save the width, height and position of each container.
Even though we are saving the width and height of each
container back to the container this is much faster because
we are saving the number and do not have to run through
any calculations again. Also, offsetHeight and offsetWidth
are both fairly slow. You would never normally notice any
performance hit from these two functions but our code is
going to be running hundreds of times each second so every
little bit helps!
Note that the biggest performance gain here, by far, comes
from not having to run through the getPosition function
hundreds of times.
*/
setAttribute('startWidth', parseInt(offsetWidth));
setAttribute('startHeight', parseInt(offsetHeight));
setAttribute('startLeft', pos.x);
setAttribute('startTop', pos.y);
}
// loop through each child element of each container
for (var j = 0; j < dragConts[i].childNodes.length; j++) {
with (dragConts[i].childNodes[j]) {
if ((nodeName == '#text') || (dragConts[i].childNodes[j] == curTarget)) continue;
var pos = getPosition(dragConts[i].childNodes[j]);
// save the width, height and position of each element
setAttribute('startWidth', parseInt(offsetWidth));
setAttribute('startHeight', parseInt(offsetHeight));
setAttribute('startLeft', pos.x);
setAttribute('startTop', pos.y);
}
}
}
}
}
// If we get in here we are dragging something
if (curTarget) {
// move our helper div to wherever the mouse is (adjusted by mouseOffset)
dragHelper.style.top = mousePos.y - mouseOffset.y;
dragHelper.style.left = mousePos.x - mouseOffset.x;
var dragConts = DragDrops[curTarget.getAttribute('DragObj')];
var activeCont = null;
var xPos = mousePos.x - mouseOffset.x + (parseInt(curTarget.getAttribute('startWidth')) / 2);
var yPos = mousePos.y - mouseOffset.y + (parseInt(curTarget.getAttribute('startHeight')) / 2);
// check each drop container to see if our target object is "inside" the container
for (var i = 0; i < dragConts.length; i++) {
with (dragConts[i]) {
if (((getAttribute('startLeft')) < xPos) &&
((getAttribute('startTop')) < yPos) &&
((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &&
((getAttribute('startTop') + getAttribute('startHeight')) > yPos)) {
/*
our target is inside of our container so save the container into
the activeCont variable and then exit the loop since we no longer
need to check the rest of the containers
*/
activeCont = dragConts[i];
// exit the for loop
break;
}
}
}
// Our target object is in one of our containers. Check to see where our div belongs
if (activeCont) {
// beforeNode will hold the first node AFTER where our div belongs
var beforeNode = null;
// loop through each child node (skipping text nodes).
for (var i = activeCont.childNodes.length - 1; i >= 0; i--) {
with (activeCont.childNodes[i]) {
if (nodeName == '#text') continue;
// if the current item is "After" the item being dragged
if (
curTarget != activeCont.childNodes[i] &&
((getAttribute('startLeft') + getAttribute('startWidth')) > xPos) &&
((getAttribute('startTop') + getAttribute('startHeight')) > yPos)) {
beforeNode = activeCont.childNodes[i];
}
}
}
// the item being dragged belongs before another item
if (beforeNode) {
if (beforeNode != curTarget.nextSibling) {
activeCont.insertBefore(curTarget, beforeNode);
}
// the item being dragged belongs at the end of the current container
} else {
if ((curTarget.nextSibling) || (curTarget.parentNode != activeCont)) {
activeCont.appendChild(curTarget);
}
}
// make our drag item visible
if (curTarget.style.display != '') {
curTarget.style.display = '';
}
} else {
// our drag item is not in a container, so hide it.
if (curTarget.style.display != 'none') {
curTarget.style.display = 'none';
}
}
}
// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;
// mouseMove target
lastTarget = target;
// track the current mouse state so we can compare against it next time
lMouseState = iMouseDown;
// this helps prevent items on the page from being highlighted while dragging
return false;
}
function mouseUp(ev) {
if (curTarget) {
// hide our helper object - it is no longer needed
dragHelper.style.display = 'none';
// if the drag item is invisible put it back where it was before moving it
if (curTarget.style.display == 'none') {
if (rootSibling) {
rootParent.insertBefore(curTarget, rootSibling);
} else {
rootParent.appendChild(curTarget);
}
}
// make sure the drag item is visible
curTarget.style.display = '';
}
curTarget = null;
iMouseDown = false;
}
function mouseDown() {
iMouseDown = true;
if (lastTarget) {
return false;
}
}
document.onmousemove = mouseMove;
document.onmousedown = mouseDown;
document.onmouseup = mouseUp;
window.onload = function () {
// Create our helper object that will show the item while dragging
dragHelper = document.createElement('DIV');
dragHelper.style.cssText = 'position:absolute;display:none;';
CreateDragContainer(
document.getElementById('DragContainer1'),
document.getElementById('DragContainer2'),
document.getElementById('DragContainer3')
);
document.body.appendChild(dragHelper);
}
function mouseCoords(ev) {
if (ev.pageX || ev.pageY) {
return { x: ev.pageX, y: ev.pageY };
}
return {
x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
y: ev.clientY + document.body.scrollTop - document.body.clientTop
};
}
</script>
<title>Drag and drop test</title>
</head>
<body>
<form id="form1" runat="server">
<!--the mouse over and dragging class are defined on each item-->
<div class="DragContainer" id="DragContainer1">
<div class="DragBox" id="Item1" overClass="OverDragBox" dragClass="DragDragBox">Item #1</div>
<div class="DragBox" id="Item2" overClass="OverDragBox" dragClass="DragDragBox">Item #2</div>
<div class="DragBox" id="Item3" overClass="OverDragBox" dragClass="DragDragBox">Item #3</div>
<div class="DragBox" id="Item4" overClass="OverDragBox" dragClass="DragDragBox">Item #4</div>
</div>
<div class="DragContainer" id="DragContainer2">
<div class="DragBox" id="Item5" overClass="OverDragBox" dragClass="DragDragBox">Item #5</div>
<div class="DragBox" id="Item6" overClass="OverDragBox" dragClass="DragDragBox">Item #6</div>
<div class="DragBox" id="Item7" overClass="OverDragBox" dragClass="DragDragBox">Item #7</div>
<div class="DragBox" id="Item8" overClass="OverDragBox" dragClass="DragDragBox">Item #8</div>
</div>
<div class="DragContainer" id="DragContainer3">
<div class="DragBox" id="Item9" overClass="OverDragBox" dragClass="DragDragBox">Item #9</div>
<div class="DragBox" id="Item10" overClass="OverDragBox" dragClass="DragDragBox">Item #10</div>
<div class="DragBox" id="Item11" overClass="OverDragBox" dragClass="DragDragBox">Item #11</div>
<div class="DragBox" id="Item12" overClass="OverDragBox" dragClass="DragDragBox">Item #12</div>
</div>
</form>
</body>
</html>
Did you miss some javascript references? Chrome is screaming that it does not find a function getMouseOffset (line 95)

Categories