Create for loop with changing image - javascript

I haven't been able to find my specific case on here yet so I thought I'd ask. I'm trying to make a very simple Tamagotchi in Javascript for a school project. Musts are that I apply DOM manipulation, use a loop, use an array(or an object), and use a function.
My idea was to make an array with all the 'emotions' as images and then a for loop to slowly count them down. Giving the impression that the mood of the Tamagotchi gets worse as time passes.
This is the code I have so far, it's not a lot:
var imgArray = ["super.png", "blij.png", "neutraal.png", "meh.png", "verdrietig.png", "dood.png"] //Array with the images
for (var i = 0; i < imgArray.length; i++)
{
//for loop that counts down array
//Here I want a function that changes the image according to the array number
}
Sorry for the bad formatting, this is my first time on here :)
This is what I have in the body:
<h1>Tamagotchi</h1>
<button id="feed">Give food</button>
<button id="play">Entertain</button>
<button id="walk">Walk</button>
<div id="tamagotchi"></div>
I'd also then like the buttons that you see above to add points to make the Tamagotchi feel better (so in the for loop the array automatically keeps ++i but I'd like the button to --i, so subtract one point) imgArray[0] is the happiest and imageArray[5] is the saddest.
I hope this wasn't too vague, please let me know if I need to better explain anything!

Here is some draft so you can start from something. I've created a function allowing you to improve the state of the tamagoshi.
For you now :
Making a function to decrease it
Make them to be displayed as image and not strings
Make it prettier using css rules
If you get trouble with the code, Stack Overflow will help. SO is not made to write code from scratch, but to fix bug and coding issues.
// Array with the images
const imgArray = [
'super.png',
'blij.png',
'neutraal.png',
'meh.png',
'verdrietig.png',
'dood.png',
];
let tamagoshiState;
// Pointer to the div where you are going to insert the picture
const tamagoshiFeel = document.getElementById('tamagotchi');
// Function that can change the state of the tamagoshi
function setTamagoshiState(i) {
tamagoshiState = i;
tamagoshiFeel.innerHTML = imgArray[i];
}
// Change the tamagoshi state in a positive way
function improveTamagoshiState() {
// Tamagoshi can't be happier
if (tamagoshiState === 0) {
return;
}
setTamagoshiState(tamagoshiState - 1);
}
// Initialize the tamagoshi state at very sad
setTamagoshiState(imgArray.length - 1);
#tamagotchi {
margin-top: 2em;
}
<h1>Tamagotchi</h1>
<button id="feed" onclick="improveTamagoshiState()">Give food</button>
<button id="play" onclick="improveTamagoshiState()">Entertain</button>
<button id="walk" onclick="improveTamagoshiState()">Walk</button>
<!-- How the tamagochi feels -->
<div id="tamagotchi">
</div>

Related

Toggle many classes

so thanks for you all that help me. You made me think what i was doing, so i short my code in what i thought would be good, but before the code was long but working now, short but not works with the first panel even that there is no errors.So maybe a loop?
So the html goes like this:
<div class="container">
<div class="down sound">
<img id="batman" class="image-1-panel active" src="flash.svg">
<img class="image-2-panel notactive" src="http://cdn.playbuzz.com/cdn/3c096341-2a6c-4ae6-bb76-3973445cfbcf/6b938520-4962-403a-9ce3-7bf298918cad.jpg">
<p class="image-3-panel notactive">Bane</p>
<p>Joker</p>
<p>Alfred</p>
</div>
And then it repeats for 3 more containers like that one. On css for the active and not active i have:
.notactive{
visibility: hidden;
position: relative;
}
.active{
position: absolute;
}
And in js:
document.querySelector('#batman').addEventListener('click', batman);
function batman(){
document.querySelector('.image-1-panel').classList.toggle('.notactive');
document.querySelector('.image-1-panel').classList.toggle('.active');
document.querySelector('.image-2-panel').classList.toggle('.active');
document.querySelector('.image-2-panel').classList.toggle('.notactive');
}
But nothing happens... I also have a for loop for sounds that is working still good, but the panels don´t move. Can someone share some light here? I thought in a loop and try it following the function but also didn´t work so clearly I am missing something, maybe wrong element I am trying to catch?
Above i have my question without the edit where you can understand what i mean.
Thanks for your help
I have 4 panels. In each panel, there will be 3 images/text one beyond the other. So it will work that when I press panel 1, panel 2,3 and 4 will turn to show an image. Press panel 2, and panel 1,3 and 4 will turn to show different image then when pressed the panel one. And so one for the rest of the panels.
So i start and i create a function for the first panel. It works, but there is any way i can make it simple? Here is the code:
function guessWho(){
document.querySelector('.image-1-panel').classList.toggle('notactive');
document.querySelector('.image-1-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('notactive');
document.querySelector('.image-3-panel').classList.toggle('active');
document.querySelector('.image-3-panel').classList.toggle('notactive');
document.querySelector('.image-4-panel').classList.toggle('active');
document.querySelector('.image-4-panel').classList.toggle('notactive1');
document.querySelector('.image-5-panel').classList.toggle('active');
document.querySelector('.image-5-panel').classList.toggle('notactive');
document.querySelector('.image-6-panel').classList.toggle('active');
document.querySelector('.image-6-panel').classList.toggle('notactive2');
document.querySelector('.image-7-panel').classList.toggle('active');
document.querySelector('.image-7-panel').classList.toggle('notactive');
document.querySelector('.image-8-panel').classList.toggle('active');
document.querySelector('.image-8-panel').classList.toggle('notactive3');
}
Any way I can put this simple? I don´t want to use any framework, so it has to be pure js. Thank you
Use a loop.
for (var i = 0; i <= 8; i++) {
document.querySelector(`.image-${i}-panel`).classList.toggle('notactive');
document.querySelector(`.image-${i}-panel`).classList.toggle('active');
}
Given that querySelector takes a string, you could build that string up inside of a loop and use the iterator inside of the string. Something like
for(i = 1; i <= 8; i++) {
document.querySelector('.image-' + i + '-panel').classList.toggle('notactive');
}
would toggle the 'notactive' class for everything that has image-x-panel.
Solution 1
for (var i = 1; i <= 8; i++) {
document.querySelector(`.image-${i}-panel`).classList.toggle('notactive');
document.querySelector(`.image-${i}-panel`).classList.toggle('active');
}
Solution 2
Add some CSS class to your HTML like CLASSNAME
And use try this
document.querySelector(`CLASSNAME`).classList.toggle('notactive');
document.querySelector(`CLASSNAME`).classList.toggle('active');
You can create a list of the new states you want your classes to be modified by. In this case, you have an object that looks like this:
{a:false,i:""}
where a is whether it's active, and i is your post class increment, like "notactive2".
Then you create a nested loop where you iterate over each number twice, and check the position in your list of states to determine what things will be added to your base "active" class.
var activeClassesList = [{a:false,i:""},{a:true,i:""},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"1"},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"2"},{a:true,i:""},{a:false,i:""},{a:true,i:""},{a:false,i:"3"}];
var imageClassPrefix = ".image-";
var imageClassPostfix = "-panel";
var start =1;
var base = 8;
for (var i=0;i<2;i++)
{
var count = 1;
for (var j=start;j<(start+base);j++) {
var modifier = activeClassesList[j-1];
var cls = (modifier.a ? "" : "not") + "active" + modifier.i;
document.querySelector(imageClassPrefix + String(count) + imageClassPostfix).classList.toggle(cls);
count++;
}
start = start + base;
}
Another approach might be to just focus on the results, so we can remove the first selectors for each image panel since they are immediately being overwritten.
That leaves the second queries for each selector. This can be simplified by combining multiple selectors inside a queryselectorAll for items with the same class being toggled.
function guessWho(){
document.querySelector('.image-1-panel').classList.toggle('active');
document.querySelector('.image-2-panel').classList.toggle('notactive');
document.querySelectorAll('.image-3-panel, .image-5-panel, .image-7-panel, .image-7-panel').forEach((f)=>{f.classList.toggle('notactive');})
document.querySelector('.image-4-panel').classList.toggle('notactive1');
document.querySelector('.image-6-panel').classList.toggle('notactive2');
}
guessWho();
.allpanels > div {display: inline-block;border:solid 1px black;height:50px;width:50px;margin:8px;}
.active { background-color:red;}
.notactive { background-color:lightsteelblue;}
.notactive1 { background-color:green;}
.notactive2 { background-color:orange;}
<div class="allpanels">
<div class="image-1-panel">1</div>
<div class="image-2-panel">2</div>
<div class="image-3-panel">3</div>
<div class="image-4-panel">4</div>
<div class="image-5-panel">5</div>
<div class="image-6-panel">6</div>
<div class="image-7-panel">7</div>
</div>

jquery - add multiple timers associated to HTML divs

I have the following DIV containing multiple cards elements:
Each of those cards have the following HTML structure:
<div class="user-w">
<div class="avatar with-status status-green">
<img alt="" src="img/avatar1.jpg">
</div>
<div class="user-info">
<div class="user-date">
12 min
</div>
<div class="user-name">
John Mayers
</div>
<div class="last-message">
What is going on, are we...
</div>
</div>
</div>
Those cards are loaded dynamically using ajax. What I need is to attach to each <div class="user-w"> a stopwatch so I can change for example background color when elapsed time is 4 min or make it hidden when elapsed time reaches 6 min.
I was thinking on using SetInterval multiple times for I think this is not possible.
Each DIV card element should be totally independant in terms of timing from the others.
Any clue on how to do it correctly?
When you build the card from the ajax object, set a data element to store the timestamp on the card. Use setInterval to trigger a function that loops through all of the cards and checks their timestamps against the current time and updates the date on the ui, changes the bgcolor, or removes the element altogether.
In general, shy away from the "attach everywhere" syndrome. Think in lists, and simple processors. You will thank yourself down the road, as will your users for more efficient code, and your maintenance programmer.
Accordingly, one thought process might be to setup an array of the elements in question, and use a single setInterval. Something like:
...
var cardList = [];
function processCards ( ) {
var i, cardEl, cardStartTime, now;
now = Date.now();
i = -1;
while ( ++i < cardList.length ) {
cardEl = cardList[ i ][ 0 ];
cardStartTime = cardList[ i ][ 1 ];
if ( cardStartTime + 6 min < now ) {
// do 6 minute thing
}
else if ( cardStartTime + 4 min < now ) {
// ...
}
}
}
$.get('/your/new/cards/call')
.done(function(...){
...
var now = Date.now();
for ( i in returnedCards ) {
cardElement = however_you_create_your_element;
attach cardElement to the DOM
// save reference to element and time created for later processing
cardList.push([cardElement, now]);
}
});
setInterval(processCards, 2*60*1000); // or whatever granularity you want.
One advantage of this approach over multiple setTimeout calls for each card is the simplicity of having a single processing function, rather than N copies lying around. It's easier to reason about and manage, and reduces the likelihood of errors if an element disappears before it's associated setTimeout function executes.
One way to do this is, after your AJAX call completes and the DOM has been updated, you can use jQuery to select your cards and for each card you can:
Get the time value and parse it to convert it to milliseconds - you can write a simple helper function for this or use something like momentjs based on how complex your requirement is
Use setTimeout with the parsed value and do any style updates/hiding as needed
Sample Code:
$('.user-w').each(function(i, el){
var $el = $(el);
var val = $el.find('div.user-date').html();
val = parseTime(val) // Assuming a function to parse time from string to milliseconds is there
setTimeout(function(){
// Do any updates here on $el (this user card)
}, val);
/*setTimeout(function(){
// Do something else when val ms is close to completion
// here on $el (this user card)
}, 0.9 * val);*/
})
If you want multiple things to happen (change bg color and then, later, hide element, for example) you can set multiple setTimeouts to happen with different time values derived from val
you want to add a function with setTimeout() for ajax success: parameter.
Ex(with jquery):-
$.ajax({
// your ajax process
success:function(){
setTimeout(function(){
$('.card-w').not('.anotherclassname').addClass('someclassname-'+i);
$('someclassname-'+i).addClass('.anotherclassname').fadeOut();
},6000);
}
})
The accepted answer can give you a lot of trouble if the ajax part
however_you_create_your_element;
attach cardElement to the DOM
Is replacing or adding elements. Ajax and processCards share cardlist and your ajax may remove items from DOM but leave them in cardlist.
You failed to mention if you replace the card list in your ajax or append new cards but the following solution would work either way.
To adjust to updating every minute and showing minutes you can change the following 2 lines:
const repeat = 1000;//repeat every second
const message = timePassed=>`${Math.round(timePassed/1000)} seconds`
to:
const repeat = 60000;//repeat every minute
const message = timePassed=>`${Math.round(timePassed/60000)} min`
(function(){//assuming cardContainer is available
const container = document.querySelector("#cardContainer");
const repeat = 1000;//repeat every second
const message = timePassed=>`${Math.round(timePassed/1000)} seconds`
const updateCards = function(){
Array.from(container.querySelectorAll(".user-w .user-date"))
.map(
(element)=>{
var started = element.getAttribute("x-started");
if(started===null){
started = Date.now()-
parseInt(element.innerText.trim().replace(/[^0-9]/g,""),10)*60000;
element.setAttribute("x-started",started);
}
return [
element,
Date.now()-parseInt(started,10)
];
}
).forEach(
([element,timePassed])=>
element.innerText = message(timePassed)
);
}
setInterval(updateCards,repeat);
}());
<div id="cardContainer">
<div class="user-w">
<div class="avatar with-status status-green">
<img alt="" src="img/avatar1.jpg">
</div>
<div class="user-info">
<div class="user-date">
12 min
</div>
<div class="user-name">
John Mayers
</div>
<div class="last-message">
What is going on, are we...
</div>
</div>
</div>
</div>

javascript for the variant of calculator

I am working on the project to create one variant of calculator. Basically, I am storing all the user clicks (which may be number and operator like +,-) in an array and handing the array to the another function when user clicks "=". I am using javascript for this. The below is my code:
var arr=[]; //array of every input from the user interface stored
var i=0; //number of input clicks from the user
var opPos=[]; //position of the operator as given by the user
var operAnd=[];//operands as given by the user
var disp='';
function clearval() {
// This function clears all the values stored in related array when C or CE is pressed
document.getElementById("op").value=0;
arr=[];
i=0;
opPos=[];
operAnd=[];
disp='';
}
//This Function get the value and supplies the array to calculating function
function getval(inp){
if(inp!="=")
{
arr[i]=inp;
disp=disp+inp;//for display in the screen of the output screen
document.getElementById("op").value=disp;
if (typeof inp!="number"){
opPos.push(i);
operAnd.push(inp);
}
i++;
}
else
{
var newInput=assembler(arr,opPos,operAnd);
clearval();
getval(parseFloat(newInput,10));
}
}
//<!------This Function calculates based on array, operator position and operands------!>
function assembler(num_array,opPos,operAnd){
var num='';
var numCollect=[];
var posCount=0;
for(var j=0;j<num_array.length;j++){
if (j==opPos[posCount]) {
numCollect.push(parseFloat(num,10));
num='';
j++;
posCount++;
}
else if (j>posCount) {
}
num=num+num_array[j];
}
num=parseFloat(num,10);
numCollect.push(num);
//document.getElementById("op").value= numCollect;
var newInput=calculator(numCollect,operAnd);
return newInput;
}
function calculator(target_num,operAnd) {
// Not the nice solution but straightforward nonetheless
var result='';
for (var l=0;l<operAnd.length;l++) {
result=result+target_num[l]+operAnd[l];
}
result=result+target_num[l];
document.getElementById("op").value=result + '=' + eval(result) ;
return eval(result);
}
I have html which has buttons like this:
<button class="num" onclick="getval(0)">0</button>
<button class="num" onclick="getval(1)">1</button>
<button class="num" onclick="getval(2)" >2</button>
<button class="num" onclick="getval(3)" >3</button>
......................... and so on
For the basic math, this code works fine and is not a problem. However, here is my problem from where I have hard time on thinking how to implement this. Say for example, I have a following button like this.
<button class='extra' onclick="Regression()"> Find Regression </button>
Now, I will have regression function which will ask user to input the regression type (1-linear, 2-quadratic and so on...this is just an example).
function regression(){
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
which is basically asking user to enter the number and click '=' to enter into the program.
Now you can see my dilemma. Anything user inputs will be firstly processed by getval() which will pass the array to another function when user clicks '=', which is not what I will want in this case. To be clear, for this kind of case which I will have many such as std. dev or some kind of functions like this, I want the keypad to behave as normal keypad and pass the value to another function without doing normal calculator stuff as it was supposed to do.
Am I thinking this straight? Any suggestions?
I think you could solve this by adding another function called passval(), which will contain much of the same logic as getval() in terms of parsing the input into a float, etc., but which doesn't ever push values onto your operand stack or call the assembler function. It simply returns the button pushed as a nice float value to the .extra function that called it.
Then, as part of the logic in your .extra functions like Regression(), you would initially swap the onclick function for all of your buttons from getval() to passval(). When the regression or other special function is complete, swap the buttons back to their default behavior.
Well, this is what I would do(if I undestood right):
Change you button layout to this:
<input type="button" class="num" value="0" /> Removing the onclick event
Always better to use input type="button" than <button>.
Create a function to bind events to the buttons:
function bindButtonEvent(func) {
var buttons; // Here some way to get the button in a collection from the DOM tree
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() { func(buttons[i]); }
}
}
With this you will got a function to change the click event of all your buttons. This will make your engine more flexible.
You will have to change your getval function a little to get the value by itself:
function getval(el){
var inp = el.value;
if(inp!="=")
So on the calculator load, you set the click function:
bindButtonEvent(getval())
When you want to call a custom behaviour function, you call the binding again:
function regression() {
bindButtonEvent(function(el) {
value = el.value;
// Do things
// When done, take bindings back.
bindButtonEvent(getval());
});
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
}
NOTE: That is an ideia. I didn't tested the code. If you're interested on this and have errors on implementation, let me know, and we'll going fixing them.

jQuery: Loop iterating through numbered classes?

I looked at the post jQuery: Loop iterating through numbered selectors? and it didn't solve my problem, and didn't look like it was truly an answer that works.
I have a list of <h3> tags that are titles to questions, and there are answers below in a <p>. I created classes for each Q & A like so:
<h3 class="sec1">Question:</h3><p class="view1">Answer...</p>
<h3 class="sec2">Question:</h3><p class="view2">Answer...</p>
<h3 class="sec3">Question:</h3><p class="view3">Answer...</p>
I used the following jQuery loop to reduce redundacy for my 21 questions.
$(document).ready(function () {
for (var i = 1; i < 21; i++) {
var link = ".sec" + i;
var content = ".view" + i;
$(link).click(function () {
$(content).toggle("fast");
});
}
});
But it isn't working for all Q & A sets, only the last one. i.e.: It works for the first set if I set the max value to 2 (only looping once). Please advise. Thanks
While I agree with #gaffleck that you should change your approach, I think it is worth while to explain how to fix the current approach.
The problem is that the click function does not get a copy of the content variable but instead has a reference to that same variable. At the end of the loop, the value is .view20. When any element is clicked it read that variable and gets back .view20.
The easiest way to solve this is to move the code into a separate function. The content variable within this function is a new variable for every call of the function.
function doIt(i){
var link = ".sec" + i;
var content = ".view" + i;
$(link).click(function () {
alert(content);
});
}
$(document).ready(function () {
for (var i = 1; i < 21; i++) {
doIt(i);
}
});
http://jsfiddle.net/TcaUg/2/
Notice in the fiddle, if you click on a question the alert has the proper number. Optionally, you could make the function inline, though I find the separate function in most cases to be a bit cleaner.
http://jsfiddle.net/TcaUg/1/
A much easier way to do this, would be this:
$(document).ready(function(){
$("h3").click(function(){
$(this).next("p").toggle("fast");
});
});
This is also safer in that you can add/remove questions and answers in the future and you won't have to update the function.
Wrap your questions in a more logical structure to create a proper scope for your questions-block:
<div id="questions">
<div class="question">
<h3 class="sec1">Question:</h3><p class="view1">Answer...</p>
</div>
<div class="question">
<h3 class="sec2">Question:</h3><p class="view2">Answer...</p>
</div>
<div class="question">
<h3 class="sec3">Question:</h3><p class="view3">Answer...</p>
</div>
</div>
Now iterate through it like this:
$(function() {
$('#questions .question h3').click(function(){
$(this).parent().find('.answer').toggle('fast');
});
});

More control when doing animations in Dashcode/Dashboard?

Recently I am giving another shot to Dashcode ;)
It's great. Is just I think is not well documented.
I have a stackLayout object with only two views in it, and a couple of buttons that interchange the views with a transition.(views show data of a large array, list)
Animations and transitions work perfect. The problem is, when I press a button while animating the animation starts again and it looks ugly (If I had n views for a datasource array of length n this should't be a problem, but this is not my case).
I want to disable buttons while animation is taking place.
Is there any callback, delegate, or any way I can get a notification when the animation is finished?
This is what I have done:
function _changeView(transitionDirection, newIndex){
//Create transition
var newTransition = new Transition(Transition.SWAP_TYPE, 0.9, Transition.EASE_TIMING);
newTransition.direction = transitionDirection;
//I only have two views. I use currentView's id to calculate not current view id and change text inside of it.
var stackLayout = document.getElementById('stackLayout').object;//stackLayout object
var nextViewId = (stackLayout.getCurrentView().id == 'view1')? '2':'1'; //
//change the text in the view that is going to appear
document.getElementById('text'+nextViewId).innerHTML = list[curIndex];
stackLayout.setCurrentViewWithTransition('view'+ nextViewId, newTransition, false);
}
function goPrevious(event)
{
curIndex--;
if(curIndex < 0){
curIndex = list.length-1;
}
_changeView(Transition.LEFT_TO_RIGHT_DIRECTION, curIndex);
}
function goNext(event)
{
curIndex++;
if(curIndex >list.length - 1){
curIndex = 0;
}
_changeView(Transition.RIGHT_TO_LEFT_DIRECTION, curIndex);
}
Eventually found the answer to this. Here's how I did it:
document.getElementById('stackLayout').object.endTransitionCallback=function(stackLayout, oldView, newView) {
//PUT CODE HERE USING stackLayout, oldView, newView to show params
}
In fact you can find all the stackLayout methods and properties in the StackLayout.js file in your project!!
Hope this helps

Categories

Resources