I'm working on a site where I have written some javascript to create a 'typewriter' type effect for the large middle centered text seen below. The script animates the text so that it types forward letter by letter and then deletes the text before starting a new word. The problem I'm having is that when the text is deleted the paragraph element containing the text is empty and the button below 'jumps' to the position where the text was. I'd like to know a good way to fix this issue, either in the javascript or with a simple css positioning fix. I wondered if there was maybe a way to position the button relative to the top "we create digital products text"?
This is my html:
<div class="agency-hero">
<section class="container">
<div class="hero-text customFadeInUp">
<h1 class="tagLine">
We create digital products
</h1>
<p><span class="txt-type " data-wait="2000" data-words='[" "," Websites "," Web Applications "]'></span></p>
<a href="agency-portfolio-4.html" class="stayPut">
See our work
</a>
</div>
</section>
</div>
and the javascript to animate the text:
const TypeWriter = function(txtElement, words, wait = 3000){
this.txtElement = txtElement;
this.words = words;
this.txt='';
this.wordIndex=0;
this.wait=parseInt(wait,10);
this.type();
this.isDeleting = false;
}
// Type Method
TypeWriter.prototype.type = function() {
//current index of word
const current = this.wordIndex % this.words.length;
//get Full text
const fullTxt = this.words[current];
//check for if currently in the deleting state or not
if(this.isDeleting){
this.txt = fullTxt.substring(0,this.txt.length -1);
}else{
//add a character
this.txt = fullTxt.substring(0,this.txt.length +1);
}
//insert txt into element
this.txtElement.innerHTML = `<span class="txt">${this.txt}</span>`;
// Initial Type Speed
let typeSpeed = 300;
if(this.isDeleting){
typeSpeed /= 2;
}
// If word is complete then move on to next word
if(!this.isDeleting && this.txt == fullTxt){
//make pause at the end
typeSpeed = this.wait;
//set Delete to True
this.isDeleting = true;
} else if(this.isDeleting && this.txt == ''){
this.isDeleting=false;
//move to next word
this.wordIndex ++;
// Pause before start typing
typeSpeed = 500;
}
setTimeout(() => this.type(),typeSpeed);
}
// Init on DOM Load
document.addEventListener('DOMContentLoaded',init);
//Init App
function init(){
const txtElement = document.querySelector('.txt-type');
const words = JSON.parse(txtElement.getAttribute('data-words'));
const wait = txtElement.getAttribute('data-wait');
new TypeWriter(txtElement, words, wait);
}
You can use CSS property min-height to maintain the desired gap between both texts. Take a look at the below codes.
With Text-
body {
background-color: lightblue;
}
h1 {
color:black;
text-align: center;
}
p {
font-family: verdana;
font-size: 40px;
background-color:red;
min-height:20px;
}
p+p {
font-size: 20px;
background-color:orange;
}
<h1>We create Digital Products</h1>
<p>Type Writer</p>
<p>See my work</p>
Without Text
body {
background-color: lightblue;
}
h1 {
color:black;
text-align: center;
}
p {
font-family: verdana;
font-size: 40px;
background-color:red;
min-height:20px;
}
p+p {
font-size: 20px;
background-color:orange;
}
<h1>We create Digital Products</h1>
<p></p>
<p>See my work</p>
p is a block element and its height is getting calculated on the basis of its content. Hope it helps.
If you're asking for relative positioning you use the css position property with a value of relative and use the top and left properties to change it. However, you could also use transform to change the positions.
For example:
button {
position:relative;
top:50vh;
}
//Or
button {
transform: translate(0, 50vh);
}
you can change these depending on how you want it.
In my opinion, if I read it correctly, it looks like you want to keep it there so I'd use absolute positioning.
Such as:
button {
position:absolute;
left:50%;
top:90vh;
//This won't move no matter what
}
You could give your <p> a defined height:
.hero-text p {
height: 20px;
}
const TypeWriter = function(txtElement, words, wait = 3000){
this.txtElement = txtElement;
this.words = words;
this.txt='';
this.wordIndex=0;
this.wait=parseInt(wait,10);
this.type();
this.isDeleting = false;
}
// Type Method
TypeWriter.prototype.type = function() {
//current index of word
const current = this.wordIndex % this.words.length;
//get Full text
const fullTxt = this.words[current];
//check for if currently in the deleting state or not
if(this.isDeleting){
this.txt = fullTxt.substring(0,this.txt.length -1);
}else{
//add a character
this.txt = fullTxt.substring(0,this.txt.length +1);
}
//insert txt into element
this.txtElement.innerHTML = `<span class="txt">${this.txt}</span>`;
// Initial Type Speed
let typeSpeed = 300;
if(this.isDeleting){
typeSpeed /= 2;
}
// If word is complete then move on to next word
if(!this.isDeleting && this.txt == fullTxt){
//make pause at the end
typeSpeed = this.wait;
//set Delete to True
this.isDeleting = true;
} else if(this.isDeleting && this.txt == ''){
this.isDeleting=false;
//move to next word
this.wordIndex ++;
// Pause before start typing
typeSpeed = 500;
}
setTimeout(() => this.type(),typeSpeed);
}
// Init on DOM Load
document.addEventListener('DOMContentLoaded',init);
//Init App
function init(){
const txtElement = document.querySelector('.txt-type');
const words = JSON.parse(txtElement.getAttribute('data-words'));
const wait = txtElement.getAttribute('data-wait');
new TypeWriter(txtElement, words, wait);
}
.hero-text p {
height: 20px;
}
<div class="agency-hero">
<section class="container">
<div class="hero-text customFadeInUp">
<h1 class="tagLine">
We create digital products
</h1>
<p><span class="txt-type " data-wait="2000" data-words='[" "," Websites "," Web Applications "]'></span></p>
<a href="agency-portfolio-4.html" class="stayPut">
See our work
</a>
</div>
</section>
</div>
Related
I have a text element that is built when the user clicks a button. In the middle of that text, I added an input element.I want to make this process repeats seven times but it looks complicated. I actually made a lot of things but here are the important ones So, First I made an input element and a button when the user enter input and click the button what he wrote will be displayed in a h1 element with a random missing word Then,I made an input element in the that the missing word should've been in.What I want is that this to repeat this process seven times.
function myFunction() {
var x = document.getElementById("input").value;
x = x + " ";
var y = document.getElementById("inputed");
var a,b = "c";
const c=[];
const d=[];
var e;
var f = 0;
const array = [];
const z = x.length;
for (let index = 0; index < 7; index++) {
a= "c";
b= "c";
c.push(0);
d.push(0);
c[index] = 0;
d[index] = 0;
e = 1;
f = 0;
//Here the program will get a random space location and the space after it
while (a != " ") {
c[index] = Math.floor(Math.random() * x.length - 2);
a = x.charAt(c[index]);
}
d[index] = c[index];
//The variable d will be the same as c and will increase until its value corrspends to a space
while (b != " ") {
d[index]++;
b = x.charAt(d[index]);
}
//I want the y.innerHTML to write from the 0 to c[0] then display the input element then from d[0] //to c[1] and display the input element again and form d[1] to c[2] until 7
y.innerHTML = `${x.slice(
0,
c[0]
)}<input type="text" style="color: gray;border:2px solid;border-radius: 10px;outline: none;
font-size: x-large;height:40px;width: 100px;" >${x.slice(d[0], z)} `;
}
}
I had problems understanding perfectly your code so I couldn't answer trying to address the exact problems affecting your specific algorithm.
Anyway I made an attempt trying to understand your description and as far as I could get I tried to implement a demo beginning from a input text filled by the user and:
When the button PARSE SENTENCE gets clicked, the procedures parses the
input text splitting its content by white spaces to find out the
words.
Then it determines how many random words he wants to "remove" from
the string and at which index.
Then it creates a span element for each of those words and put all of them
in a separate array
Such spans, will have the word in their innerText, the default class
word and an added class that will be kept-word if that's a word not
to remove and removed-word otherwise. Plus they will keep track of that original word inside a data attribute called data-original
Those spans get appended to the #transformed element, and at this
stage, if the word belongs to the class .removed-word its
innerText gets emptied and its contenteditable attribute
activated.
In the end, after the parsing of the sentence, the exact spans code
will be shown in the <pre> element to give evidence of what's going
on
I know it's very far from what you have asked.. considering I didn't strictly stressed the algorithm aspect and mostly used shortcut like split, includes, contains and more importantly I totally changed your logics and used spans for words parsed and didn't use the input at all but contenteditable.
I sincerely hope it will anyway give some inspiration
function transform(){
const input = document.getElementById('input').value.trim();
const words = input.split(" ");
const nrOfWordsToReplace = Math.floor(Math.random() * words.length);
const indexesOfWordsToReplace
= Array.from({length: nrOfWordsToReplace}, () => {
return Math.floor(Math.random() * words.length);
});
//for each word in the input, create a span element to add in #transformed
//words expected to be kept will have their span with class .word.kept-word
//words expected to be removed will have their span with class .word.remove-word
let index = 0;
const outputWords = [];
for(word of words){
if( indexesOfWordsToReplace.includes(index) ){
const outputWord = createOutputWordElement(word, 'removed-word');
outputWords.push( outputWord );
}else{
const outputWord = createOutputWordElement(word, 'kept-word');
outputWords.push( outputWord );
}
index++;
}
const codeContainer = document.querySelector('pre');
codeContainer.innerHTML = '';
//clear its content appends those spans to #transform
const transformed = document.getElementById('transformed');
transformed.innerHTML = '';
for(outputWord of outputWords){
if (outputWord.classList.contains('removed-word')){
outputWord.innerText = '';
outputWord.contentEditable = true;
}
transformed.append(outputWord);
const code = document.createElement('code');
code.innerText = outputWord.outerHTML;
codeContainer.append(code);
}
}
//returns a span element having the word as content and classname among its classes
function createOutputWordElement(word, classname){
const outputWord = document.createElement('span');
outputWord.classList.add('word');
outputWord.classList.add(classname);
outputWord.dataset.original = word;
outputWord.innerText = word;
return outputWord;
}
body{
padding: 1rem;
}
*{
box-sizing: border-box;
}
button{
width: 100%;
margin-top: 1rem;
margin-bottom: 1rem;
padding: 1rem;
cursor: pointer;
text-transform: uppercase;
}
#input{
display: block;
width: 100%;
}
pre code{
display: block;
}
#transformed{
border: dashed 3px darkgray;
padding: 1rem;
}
.word{
border: solid 2px;
padding: .25rem;
margin-right: .50rem;
display: inline-block;
}
.word.kept-word{
border-color: darkgray;
}
.word.removed-word{
border-color: red;
cursor: pointer;
min-width: 2rem;
}
.
<input type="text" id="input">
<button type="button" onclick="transform();">Parse sentence</button>
<div id="transformed"></div>
<pre>
</pre>
I'am working on progressbar that change the level when reaching some point.
The code.
I want to change the text under the progressbar when the progressbar reach 100 or above 90.
I could change it once but I want to go to next level, like this.
silver ==> gold ==> diamond ==> and more
const step = 5;
var content=document.getElementById('mylevel').innerHTML;
const updateProgress = () => {
const currentWidth = Number(document.getElementById("progressvalue").style.width.replace( "%", ""));
if (currentWidth>=100) {
return;
}
else {
document.getElementById("progressvalue").style.width = `${currentWidth+step}%`;
}
if (currentWidth > 90) {
document.getElementById("mylevel").textContent = "gold";
document.getElementById("progressvalue").style.width = "0%";
}
if (currentWidth > 90 && content == "gold") {
document.getElementById("mylevel").textContent = "diamond";
document.getElementById("progressvalue").style.width = "0%";
}
}
const restart = () => {
document.getElementById("progressvalue").style.width = "0%";
}
.progress {
background-color: #ededed;
width: 100%;
overflow: hidden;
}
#progressvalue {
height: 40px;
background-color: lightgreen;
width: 0%;
}
<div class="progress">
<div id="progressvalue"></div>
</div>
<p id="mylevel">silver</p>
<br />
<button type="button" onclick="updateProgress()">
Update Progress
</button>
<button type="button" onclick="restart()">
Restart
</button>
When the updateprogress is above 90 the silver change to gold, but I need to change again to diamond when the updateprogress is again above 90.
Am I putting the if condition in a wrong place, I tried many times.
I don't know what I'am missing and am new with JavaScript
I started the code but got help here to make it much better (80% of the code done by
teresaPap thanks)
Update
After closer inspection it is an issue of content not updating you need to put it inside updateProgress() or it will forever remain the initial value.
const step = 5;
const updateProgress = () => {
var content = document.getElementById('mylevel').innerHTML;
//the rest of the code
I do however recommend you to improve your if statements. You only need one if for this task.
A better solution
A better solution would be something like this:
Add a hidden value to keep your level progress
</div>
<p id="hiddenlevel">0</p>
<p id="mylevel">silver</p>
<br />
and css:
#hiddenlevel {
height: 0px;
visibility: hidden;
width: 0%;
}
now that you have a hidden value you can wrap up all future ifs in a single one.
const levels = ["silver", "gold", "diamond"]
var mylevel = Number(document.getElementById("hiddenlevel").innerHTML);
if(currentWidth > 90 && mylevel < levels.length){
document.getElementById("hiddenlevel").textContent = mylevel + 1;
document.getElementById("mylevel").textContent = levels[mylevel + 1];
document.getElementById("progressvalue").style.width = "0%";
}
and just like that you can just add a new level inside the levels array and it will be added without issues.
Update 2
Just noticed I made a mistake!
You don't need a hidden element for this: you might end up having to use hidden elements when using plugins, but it was completely unnecessary here :)
Updated code:
const step = 5;
var mylevel = 0;
const updateProgress = () => {
const currentWidth = Number(document.getElementById("progressvalue").style.width.replace( "%", ""));
if (currentWidth>=100) {
return;
}
else {
document.getElementById("progressvalue").style.width = `${currentWidth+step}%`;
}
const levels = ["silver", "gold", "diamond"];
if(currentWidth > 90 && mylevel < levels.length){
mylevel = mylevel + 1;
document.getElementById("mylevel").textContent = levels[mylevel];
document.getElementById("progressvalue").style.width = "0%";
}
}
const restart = () => {
document.getElementById("progressvalue").style.width = "0%";
}
.progress {
background-color: #ededed;
width: 100%;
overflow: hidden;
}
#progressvalue {
height: 40px;
background-color: lightgreen;
width: 0%;
}
<div class="progress">
<div id="progressvalue"></div>
</div>
<p id="mylevel">silver</p>
<br />
<button type="button" onclick="updateProgress()">
Update Progress
</button>
<button type="button" onclick="restart()">
Restart
</button>
I need to create a lottery game. I have an idea of how the whole code should be and it's mostly done, but I have a problem:
I have a button with an onclick called generar(), and this function, needs to draw 3 numbers from 1 to 999 and create along with the three numbers, another button which after pressing it, draws another number and then (the new button) checks the new number number with the ones who are already created to see if I won, or not.
Okay, I've managed to do the whole code, but the comparison of the three first numbers with the fourth number, doesn't work correctly. Always, the comprobation which says if I won or not, writes "¡Perdedor!" (looser!).
This is my code:
<html>
<head>
<title>Práctica 3</title>
<style>
#contenedor { border: 1px black solid;
width: 250px;
height: 250px;
float: left;
margin-right: 5px;
text-align: center;
margin-bottom: 5px; }
</style>
</head>
<body>
<div id="contenedor"> <p id="primerNumero"/> </div>
<div id="contenedor"> <p id="segundoNumero"/> </div>
<div id="contenedor"> <p id="tercerNumero"/> </div>
<button id="generar" onclick="generar()">Genera dècims</button>
<div id="contenedor"> <p id="resultado"/> </div>
<p id="t"/>
<script>
function generar() {
var primerNumero = Math.floor(Math.random() * 10);
var segundoNumero = Math.floor(Math.random() * 10);
var tercerNumero = Math.floor(Math.random() * 10);
var botones = document.getElementsByTagName("BUTTON").length;
if (botones < 2) {
generarBotones();
}
document.getElementById("primerNumero").innerHTML = primerNumero;
document.getElementById("primerNumero").style.fontSize = "72px";
document.getElementById("segundoNumero").innerHTML = segundoNumero;
document.getElementById("segundoNumero").style.fontSize = "72px";
document.getElementById("tercerNumero").innerHTML = tercerNumero;
document.getElementById("tercerNumero").style.fontSize = "72px";
}
function generarBotones() {
var boton = document.createElement("BUTTON");
var t = document.createTextNode("Realizar sorteig");
boton.onclick = function(){
var numeroGanador = Math.floor(Math.random() * 10);
document.getElementById("resultado").innerHTML = numeroGanador;
document.getElementById("resultado").style.fontSize = "72px";
if(primerNumero == numeroGanador || segundoNumero == numeroGanador || tercerNumero == numeroGanador) {
document.getElementById("t").innerHTML = "¡Ganador!";
} else {
document.getElementById("t").innerHTML = "¡Perdedor!";
}
};
boton.appendChild(t);
document.body.appendChild(boton);
}
</script>
</body>
</html>
If somebody can tell me why and where I fail and a possible solution, it will be great. I will very grateful if an explanation is given too
primerNumero
segundoNumero
tercerNumero
Are html elements, not value, to get the value is necessary to use innerText OR innerHTML.
if(primerNumero.innerText == numeroGanador || segundoNumero.innerText == numeroGanador || tercerNumero.innerText == numeroGanador) {
document.getElementById("t").innerHTML = "¡Ganador!";
} else {
document.getElementById("t").innerHTML = "¡Perdedor!";
}
Or you can rename the variables to a different name of the element, because your problem is that the variables that contain the generated numbers are the same name as the id of the html element. If rename will work according to your first code.
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>
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..