Problems with flipping cards (vanilla JS) - javascript

I'm trying to solve 2 problems with this project:
Flip the cards back when no match is made.
Once the randomization occurs, allow the user to briefly show all the randomized cards for x seconds before the game timer commences.
At one point I was able to flip the unmatched cards, but I lost that ability whilst trying to sort out problem #2.
For #1 I first declared a variable:
const pix = document.querySelectorAll('.card img');
Then I did an if/else to hide unmatched:
function checkForMatch() {
if (
toggledCards[0].firstElementChild.className === toggledCards[1].firstElementChild.className) {
toggledCards[0].classList.toggle('match');
toggledCards[1].classList.toggle('match');
toggledCards = [];
matched++;
} else {
setTimeout(() => {
toggleCard(toggledCards[0]);
toggleCard(toggledCards[1]);
toggledCards = [];
// Trying to hide unmatchaed cards here
pix.style.display = "none";
}, 1000);
}
};
That fails and I cannot figure out why.

As Sheng Slogar have mentioned, You don't have class names on your img tags so it doesn't work. I added some to check and it was working, so just add classes with numbers to images and it will work properly :)
I also recommend to use data attributes for this kind of functionality as it's exactly what they are made for.

Related

Carousel component based on progress moving bar

I have the following mockup:
Until now, I managed to do the following which is pretty close: https://codesandbox.io/s/codepen-with-react-forked-16t6rv?file=/src/components/TrendingNFTs.js
I have the following problems that I'm trying to tackle:
When pressing the next and previous buttons, the component seems to reset, what I want is to stop there in case the element is the last on the list
I'm find the progress bar below the cards with the arrows hard to implement and I want to ask if you guys have ever seems something that comes close to this.
Thanks in advance, if something is willing to help me with some ideas at least.
You can take let count = 0 at the start and update rest of the code as
const shiftPrev = (copy) => {
let lastcard = copy.pop();
if (count > 0) {
copy.splice(0, 0, lastcard);
setCarouselItems(copy);
}
};
const shiftNext = (copy) => {
let firstcard = copy.shift();
if (count < copy.length) {
copy.splice(copy.length, 0, firstcard);
setCarouselItems(copy);
}
};
The logic is you take the initial count (you can also keep your count in state but on setState it will re-render which might cause performance issue later)
Also, please consider adding css and the initial card its displaying on UI and set your start/end count accordingly.

Function is repeating same values multiple times

I am writing some javascript code to create a flashcard system. In this system, a flashcard is displayed to the user and he decides whether the card is difficult or easy. In this way, each card is sorted into two different groups that will be used later. For the following code, arr is the flashcard list while the difficultList and easyList are the two lists. I have also used jquery to run a function according to the class. So, if the button with difficult flashcard is clicked, difficultList is changed. Do note that as of now, the end() function simply logs 'END' to the console. Finally, the text variable used in nothing but an HTML element with id of text whose value is changed to display the question.
function mainFunc() {
if (indexVal > arr.length) {
end();
} else {
console.log(indexVal);
text.innerHTML = arr[indexVal][0];
$(".difficult").click(function () {
indexVal += 1;
difficultList.push(arr[indexVal]);
mainFunc();
})
$(".easy").click(function () {
indexVal += 1;
mainFunc();
easyList.push(arr[indexVal]);
})
}
}
mainFunc();
When the code runs for the first time, everything works fine. But when I click on any of the buttons during second flashcard, an error occurs and the value of indexVal increases by 2 instead of 1, causing some unexpected behaviour. I have made several attempts in debugging the code but to no avail. Any help would be greatly appreciated (the problem with the code, a new solution etc). Thank you!

how to change state on window resize?

I am trying to create a multiple items per slide carousel that i need to change the number of items per slide with different screen breakpoints.
For example, i want to show 4 items per slide when window width is bigger than 1200px, 3 between (992px - 1200px), 2 between (768 - 992px) and 1 for smaller screens.
The idea that i got in my mind is to make an event that change a state when a certain breakpoint reached:
state: {
itemsNum: 1
}
const createSlides = () => {
let slides = [];
let itemsNum = this.state.itemsNum;
for (let i = 0; i < products.length ; i = i + itemsNum) {
slides.push(products.slice(i, i + itemsNum))
}
return slides;
}
const slides = createSlides ();
slides.map (........)// rendering the items in the slide
This is just the incomplete idea in my mind, but i need the complete solution that make this idea work successfully.
What is the better way to achieve that in the term of react??
You can use window.onresize.
window.onresize = function(event) {
... };
Well, to talk about my opinion, you can use "event handlers".
Like this: window.addEventListener('onresize', createSlides)
As the time goes, I didn't complete the whole things through, but if you want to know more about this or you are interested in this method, I can tell you the exact method.

Reset counter variable on condition

I have this simple "pagination" counter which is fetching the next page from an API. It works fine but the problem is that whenever I change category (movies or series) the counter obviously doesn't reset so instead of fetching the first page of the new category it continues from the number it left off.
I tried numerous conditional combinations but nothing really worked so far. I'm sure it's not that hard to solve I just can't think of the right logic to use.
let page = 1;
document.getElementById('load-more').addEventListener('click', () => {
page++;
const movies = document.getElementById('movies');
const series = document.getElementById('series');
if(movies.classList.contains('active-link')) {
getMovies(page);
} else if (series.classList.contains('active-link')) {
getSeries(page);
}
})
Reseting the let counter inside the if..else doesn't really work because every time I click the load more button it resets it back to page 1.
Use separate variables for the current movies page and the current series page. Also note that you can simplify your logic by using a single querySelector instead of selections followed by classList.contains:
let moviesPage = 1;
let seriesPage = 1;
document.getElementById('load-more').addEventListener('click', () => {
if (document.querySelector('#movies.active-link')) {
moviesPage++;
getMovies(moviesPage);
} else if (document.querySelector('#series.active-link')) {
seriesPage++;
getSeries(seriesPage);
}
});
Set another event listener for the click on your #movies and #series link,
and set the page variable to 1 at this place.
That would be the usual behavior when switching lists, one usually see a reset of paging.

Javascript: Can't handle events for multiple instances

I have put together a fun API for game creation. In the code I create a prototype for Mover and then extend it with several specific prototypes (Gold, Monster, and Hero). Each one is based on a img tag with a given ID. I use type-specific information in the constructor and a single template method for each type. Most of the functional code in Mover depends on those type-specific details. I have included one example for simplicity.
I use method calls in a separate script to create and destroy instances of the Mover child types. When I create and destroy one instance at a time everything works as intended. The image updates, the sound plays and it is removed after the correct delay. If I create two or more, however, only the last one works as expected. So if I make gold, moster, hero. Only the hero will remove correctly. The other two will play the audio, but don't appear to update.
I ran into the same problem when I tried to attach a function to the onclick event for more than one instance. Only the last one worked and the others did nothing. Obviously I'm missing something about the way java handles method assignments. Any explanation you can offer would help.
Thanks,
BSD
function Mover()
{
}
Mover.prototype.InitTag = function()
{
this.HTMLtag.src=this.imageURL;
this.HTMLtag.style.position="absolute";
this.HTMLtag.style.width=characterSize;
this.HTMLtag.style.height=characterSize;
this.Position(Math.floor(Math.random()*(MaxW-characterSize)+(characterSize/2)),Math.floor(Math.random()*(MaxH-characterSize)+(characterSize/2)));
}
Mover.prototype.Destroy = function()
{
var disp = this.HTMLtag.display;
this.HTMLtag.src=this.destroyURL
this.HTMLtag.display = disp;
this.destroyAudio.play();
this.RemoveTag();
}
function Monster(id)
{
this.MonsterID = id;
this.HTMLtag = document.getElementById("monster"+id);
this.imageURL = "monster1.jpg";
this.destroyURL = "monster2.jpg";
this.destroyAudio = monsterAudio;
}
Monster.prototype = new Mover();
Monster.prototype.RemoveTag = function()
{
var mID = this.MonsterID;
setTimeout(function() {field.DeleteMonster(mID)}, 1000);
}
function Hero()
{
this.HTMLtag = document.getElementById("hero");
this.imageURL = "hero1.jpg";
this.destroyURL = "hero2.jpg";
this.destroyAudio = heroAudio;
}
Hero.prototype = new Mover();
Hero.prototype.RemoveTag = function()
{
setTimeout(function() {field.DeleteHero()}, 5000);
}
function Gold(id)
{
this.GoldID = id;
this.HTMLtag = document.getElementById("gold"+id);
this.imageURL = "gold1.jpg";
this.destroyURL = "gold2.jpg";
this.destroyAudio = goldAudio;
}
Gold.prototype = new Mover();
Gold.prototype.RemoveTag = function()
{
var mID = this.GoldID;
setTimeout(function() {field.DeleteGold(mID)}, 1000);
}
---------UPDATE UPDATE UPDATE-----------
I have at least partially fixed the problem. I have gotten it to work, but I still don't know why it didn't function as intended. I noticed that while my browser's (Chrome) developer tools could visually identify the most-recently-added Mover when it was being destroyed, it could not do so with the any other movers.
Tag of most recently added Mover can be identified in Chrome developer tools.
This suggested that Mover.HTMLtag was not actually the same as document.getElementById('mover1'). I was able to confirm this by looking at the variables in the GoldField.DeleteMover. At the line indicated mover.src has not changed, but movers[id].HTMLtag.src has been correctly updated. In the most-recently-added case they were both the same.
GoldField.prototype.DeleteMover = function(id)
{
var isHero = false;
if(this.Hero!=null && id==this.Hero.myID)
{
this.Hero = null;
isHero = true;
}
else if(this.Tower!=null && id==this.Tower.myID)
{
this.Tower = null;
}
var mover = document.getElementById("mover"+id);
if(!isHero)
{
this.tag.removeChild(mover);//<<< HERE HERE HERE HERE
delete this.movers[id];
}
}
So, I changed one line in Mover.Destroy. By finding the tag by ID and setting the src. I was able to reliable behavior. It would appear that Mover.HTMLtag is not reliable the same after the second Mover is added. Any explanation?
Mover.prototype.Destroy = function()
{
document.getElementById(this.HTMLtag.id).src=this.destroyURL;
this.HTMLtag.src=this.destroyURL;//old method
this.destroyAudio.play();
this.RemoveTag();
}
On suspicion that this might extend to other updates to this.HTMLtag I set up some basic movement of the Hero. It works great, but if you add one additional Mover of any kind it no longer moves. That narrows down the question considerably. Why would constructing a second Mover cause the prototype members to change?
So I debug your code and I found the cause of your problem. The problem was when you create a new instance of monster you storing a reference to it on the monster var. And when you delete it you don't delete / update the reference to it. So your delete function myField.DeleteMover(id) try to delete a monster already deleted. How to solve this.
// create an array to keep ref to our instances
var monsters= [];
// var monster = null;
function addMonster()
{
// monster = goldField.AddMonster();⏎
// push every monster in array
monsters.push(goldField.AddMonster());
}
function killMonster()
{
// if array.length is true
if (monsters.length) {
// call the destroy function on the last ref
monsters[monsters.length - 1].Destroy();
// remove the last ref from array using pop
monsters.pop();
}
//monster.Destroy();
}
This is working however I think all of this should be done in the objects itself. And you should not care about it here.
Another advice try to use more array methods. Avoid using delete on array index because it mess with index and count instead use splice(index, 1) same for add item in array use push instead of arbitrary index.
Anyway funny game! Good luck to finish it.
Edit, after your answer I go back an test.
To make it work I do this.
// First go inGoldField.prototype.DeleteMover and replace the ugly delete index by
this.movers.splice(id, 1);
// Then in the Mover.prototype.Destroy
// This part is a a little blurred for me.
// the current HTMLtag looks good but when I console.log like this
console.log('before', this.HTMLtag);
this.HTMLtag = document.querySelector("#mover" + this.myID);
console.log('after', this.HTMLtag);
// They are not equal look like the first is outdated
You should convert all your delete and add to splice and push methods.
This is just a quick debug I don't know why the selector is outdated.
So I check the code again and I make it work without refreshing the selector. The problem is caused by the creation of dom element with innerHTML.
First reset
this.HTMLtag.src=this.destroyURL
Then instead of
//Mover.prototype.Destroy
this.tag.innerHTML+="<img id='mover"+this.moverCount+"'>";
I create a img dom el.
var img = document.createElement("img");
img.setAttribute('id', 'mover' + this.moverCount);
this.tag.appendChild(img);
All Monsters are now deleted with the image.
I don't check for the hero but first you should update your innerHTML and reply if there is still a problem. I don't think there is any problem with some prototype.

Categories

Resources