Creating multiples of the same DOM Element Image - javascript

I am a new Developer and am working on a Tic Tac Toe SPA. I have a working game, but I want to customize the game tokens. I have tried creating a DOM element a few different ways, which were all successful. Here is the problem:
Every time I go to make a second move for a player, the DOM image disappears and reappears in the new square selected. Obviously this is not the desired action. Is there something I don't know about creating a DOM element. I have googled this and read countless articles and watched countless videos.
const stark = document.createElement('img')
stark.src = 'https://i.imgur.com/d70XlET.png'
stark.height = 80
stark.width = 80
const lanister = document.createElement('img')
lanister.src = 'https://i.imgur.com/d70XlET.png'
lanister.height = 80
lanister.width = 80
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$('#' + event.target.id).append(turn)
}
}
}
'turn' is a variable that works with a toggle function to switch between players and stores whichever players turn it is(i.e. 'stark')
I would be really grateful if someone could point me in the direction of a resource where I could learn more about this.
const player1 = stark
const player2 = lanister
let turn = player1
let prevTurn = player2
const togglePrevTurn = () => {
if (!gameOver) {
if (prevTurn === player1) {
prevTurn = player2
} else {
prevTurn = player1
}
}
}
const toggleTurn = () => {
if (!gameOver) {
if (turn === player1) {
turn = player2
} else {
turn = player1
}
$('#message').text(turn + " 's turn")
}
}

Whenever you use Javascript's appendChild or jQuery's append, when you pass it an element, that element gets removed from its previous location in the DOM (if it's in the DOM), and then gets inserted at the new position. It sounds like what you need to do is explicitly create a new element each time, which you might do with cloneNode().
Also, probably best to name your variables precisely - if turn is an image, name it to make it clear that it's an image, perhaps currentPlayerImage.
In addition, because you already have a reference to the event.target, there's no need to reselect it with $('#' + event.target.id) - just select event.target:
const play = (event) => {
if (gameOver === false) {
if (event.target.innerHTML === '') {
$(event.target).append(currentPlayerImage.cloneNode());
}
}
}

Related

Slow Render/Delayed Render in Vanilla Javascript App

I have a couple issues - the app is a vanilla javascript app for school that maps audio to each alphabetical key. The issues are as follows:
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.
To look at the bugs yourself, live link is here: https://haeuncreative.github.io/mosatic/
relevant code:
if (document.readyState === "interactive") {
const allAudio = document.querySelectorAll("audio")
console.log(allAudio)
if (allAudio.entries(audio => (audio.readyState === 4)))
window.addEventListener('load', function() {
const keysDown = new KeyDownHandler()
const canvas = document.querySelector('canvas');
const context = canvas.getContext('2d');
context.fillStyle = '#967bb6'
context.fillRect(0, 0, 1000, 562.5)
const clickDown = new ClickHandler(keysDown)
}
);
}
export default class KeyDownHandler {
constructor() {
this.addPressListener()
// audio
this.soundBank = new AudioBank
this.soundBank.createBank(CONSTANTS.KEY_ALPHABET)
// visual // intro
this.intro1 = document.querySelector('#intro1')
this.intro2a = document.querySelector('#intro2a')
this.intro2b = document.querySelector('#intro2b')
this.intro2c = document.querySelector('#intro2c')
this.intro3 = document.querySelector('#intro3')
this.intro4 = document.querySelector('#intro4')
this.introBank = [
this.intro1,
this.intro2a,
this.intro2b,
this.intro2c,
this.intro3,
this.intro4
]
// visual // main
this.body = document.querySelector('body')
this.canvas = document.querySelector('canvas');
this.context = this.canvas.getContext('2d');
this.background_colors = ["#95c88c", "#967bb6", "#A7C7E7", "#FF6961"]
this.aniBank = new AniBank;
// recording/user interaction
this.keys = [];
this.durations = [];
this.recording = false;
}
introSwitch() {
this.soundBank.playSpace()
if (this.currentSlide) {
this.currentSlide.style.animation = "fadeOut 1s"
this.currentSlide.style.display = "none"
this.currentSlide = ""
}
if (!this.introBank.length) {
slide.style.display = "none"
this.introFinish = true
}
if (this.introBank.length) {
let slide = (this.introBank.shift())
slide.style.filter = "brightness(60%)"
console.log(slide)
if (slide === this.intro4 || !slide) {
this.canvas.style.display = "flex";
this.addKeyListeners()
}
if (slide.style.display = "none") {
slide.style.display = "flex"
slide.style.filter = "brightness(60%)"
}
this.currentSlide = slide;
slide.style.filter = "brightness(100%)"
}
}
addPressListener() {
window.addEventListener('keypress', e => {
e.preventDefault()
e.stopImmediatePropagation()
this.introSwitch()
if (this.currentSlide === this.intro4) {
this.currentSlide.style.display = "none"
window.removeEventListener("keypress", this.introSwitch)
this.addKeyListeners()
}
})
}`
It takes quite a while to load the audio - the app proceeds like normal but with no audio - it usually can run as intended after a few minutes or a few refreshes, but I do want to get rid of that period. I tried to fix that using (document.readyState === "interactive") and if (allAudio.entries(audio => (audio.readyState === 4)))
It doesn't take the first key down event to change the intro slides, it takes the second - but I'm also not sure how to fix this. The slides are in an array that goes through each item and then makes them display as none. I also tried to add the event listener for the keyboard earlier, but to no avail.

matter.js: collisionStart triggered many times for one collision

I am working on a game app using React native and Matter.js.
I am trying to implement a system that adds points every time a bullet hits a target.
In order to do this, I am trying to use collisionStart to detect the collision.
However, even though the bullet and target collide only once, the event seems to be triggered 41 times.
This is the code:
Matter.Events.on(engine, 'collisionStart', (event) => {
let pairs = event.pairs
for (const pair of pairs) {
if (pair.bodyA.label === 'bullet' && pair.bodyB.label === 'Worm') {
console.log("target hit");
}
}
})
In the end, I'm planning to replace console.log with something that adds points. At the current moment, one collision seems like it would trigger the add points 41 times, which is obviously not ideal.
Any ideas what is happening here and how I can get this to trigger only once for one collision?
Try next example. I take it from my own project [you need little adaptd from ts to js]:
Matter.Events.on(this.starter.getEngine(), "collisionStart", function (event) {
root.collisionCheck(event, true);
});
public collisionCheck(event, ground: boolean) {
const myInstance = this;
const pairs = event.pairs;
for (let i = 0, j = pairs.length; i !== j; ++i) {
const pair = pairs[i];
if (pair.activeContacts) {
if (pair.bodyA.label === "bullet" && pair.bodyB.label === "Worm") {
const collectitem = pair.bodyA;
this.playerDie(collectitem);
} else if (pair.bodyB.label === "bullet" && pair.bodyA.label === "Worm") {
const collectitem = pair.bodyB;
this.playerDie(collectitem);
}
// ....
}
}
}
public destroyBody = (destroyBody) => {
try {
Matter.Composite.remove(this.getWorld(), destroyBody);
} catch(err) {
console.log(err)
}
}
If you still have same problem , we can adapt also with flag PREVENT_DOUBLE_BY_1_SECOUND for example.

changing self typing text via select button not working correctly

https://jsfiddle.net/AlexThunders/k8s79zL5/18/
I'm trying to change self typing text from English to Russian when I click option in select:
let engType = [
'Never give up. ',
'You can win. '
]
let rusType = [
'Никогда не сдавайся. ',
'Ты можешь победить. '
]
page is loaded and this function gradually types letters:
function typeCharacters(phrases) {
if(phrases[count] !== undefined && phrases[count] !== null && phrases[count] !== "") {
let allLength = phrases[count].length
setInterval(() => {
if(phrases[count] !== undefined && typePar !== null) {
character = phrases[count].slice(ind,ind+1)
txt = document.createTextNode(character)
typePar.appendChild(txt)
ind++
let typeLength = typePar.textContent.length
if(typeLength === allLength) {
count++
ind = 0
if(phrases[count] !== undefined) {
allLength += phrases[count].length
}
}
}
},100)
}
}
typeCharacters(engType)
It works. But when I merely touch select button without even choosing language, I get nonsense paragraph with mixed letters in one or in both languages within the same paragraph:
function searchLang(choosenLang) {
//if choosen language coincides with one in Object:
if(languages[choosenLang]) {
allDataElements.forEach(element => {
//every property of choosen object/language
for(let x in languages[choosenLang]) {
//compare with element's data attribute
if(element.getAttribute('data') === x) {
//the same attribute changes iinerText in accordance with object
element.innerText = languages[choosenLang][x]
if(languages[choosenLang].changePhrases !== undefined) {
languages[choosenLang].changePhrases(choosenLang)
}
}
}
})
}
}
select.addEventListener('click', () => {
allLangOptions.forEach(option => {
if(option.selected === true) {
let lang = option.value
searchLang(lang)
}
})
})
and the result:
Никeгда не сд.вайся. Тыuможешь по едить. OR
Никогда нe up. айс
However for other html elements select button works right: only when I choose option but not click select itself.
I use changePhrases functions in object to change language in typing paragraph:
let languages = {
en: {
mPheadLIabout: 'About',
mPheadLIprojects: 'Projects',
mPheadLIcontacts: 'Contacts',
changePhrases: function() {
if(typePar !== null) {
typePar.textContent = "";
count = 0
ind = 0
typeCharacters(engType)
}
}
},
ru: {
mPheadLIabout: 'О сайте',
mPheadLIprojects: 'Проекты',
mPheadLIcontacts: 'Контакты',
changePhrases: function() {
if(typePar !== null) {
typePar.textContent = "";
count = 0
ind = 0
typeCharacters(rusType)
}
}
}
}
At first paragraph clears itself, and begins to type from first character as indicated above.
I've tried to use variable reset to stop invoking typing English characters but unsuccessfully.
Also I've applied different variants with setTimeout and promise for case when paragraph is cleared and only then you run function typeCharacters(rusType). Still not working and no errors in console.
And the same result I get with English.
Looks like when I click select button(not options) it again fires function to type text, not waits till I use options. And when I click option it fires to times simultaneously.
Here is the entire code and weird result:
https://jsfiddle.net/AlexThunders/k8s79zL5/18/
Instead of binding an event handler to the click event, you should only fire the callback when a user changes the selected option of a select element. So the event type should be change:
select.addEventListener('change', () => {
allLangOptions.forEach((option) => {
if (option.selected === true) {
let lang = option.value;
searchLang(lang);
}
});
});
This way you can also use your keyboard to change the options. Although keyboard users could change the language option, as this is not a mouse click, your click event would not fire and the callback inside your addEventListener would not run. Make sure you always hook to the change event of the select element.
Another problem is that you never cancel the interval. When you select a language, it just starts another timer which just messes up everything. It will look like the effect is sped up, the text become gibberish as the different characters are getting mixed, etc.
To avoid this, you need to save the interval ID returned from window.setInterval() and cancel it when you run typeCharacters():
// ...
let reset = false;
// create a variable in an outer scope
let activeInterval = null;
function typeCharacters(phrases) {
// clear running interval
clearInterval(activeInterval);
if (reset) return;
if(phrases[count] !== undefined
&& phrases[count] !== null
&& phrases[count] !== "") {
let allLength = phrases[count].length
// save the interval ID
activeInterval = setInterval(() => {
if(phrases[count] !== undefined && typePar !== null) {
// ... rest of your code

What is "onkeyup" in gamepad api?

I'm experimenting a little with gamepad api. When a button is pressed, it gets "fired" all the time until the button is released. This is cool for something like running or shooting in a game, but problematic, when I want to navigate inside a menu (e.g. going 16 menu items down instead of one).
On a normal keyboard, I would use onkeyup/onkeydown to see, when a button press has ended. But on gamepad api, I didn't find something like that.
JS:
var gamepadPressCounter = 0;
function update() {
const gamepads = navigator.getGamepads();
if (gamepads[0].buttons[0].pressed === true) {
gamepadPressCounter++;
}
window.requestAnimationFrame(update);
}
So, do you know a way to get the gamepadPressCounter only one up per press and not infinite times?
You have to check it against whether it was pressed last time you checked. It doesn't fire events, it is stateful. You are directly checking if the button is pressed or not.
Make a variable to store the state of the button last time you checked and compare it to see if it has changed from last time.
const anyButtonPressedChangeListener = {
listeners: [],
addListener(cb) {
this.listeners.push(cb);
},
signal(state) {
for (const cb of this.listeners) {
cb(state);
}
}
}
let buttonstate = false;
const loop = () => {
const gamepads = navigator.getGamepads();
const gamepad = gamepads[0];
if (gamepad) {
const statenow = gamepad.buttons.some(btn => btn.pressed);
if (buttonstate !== statenow) {
buttonstate = statenow;
anyButtonPressedChangeListener.signal(statenow);
}
} else {
console.log("Not connected");
}
setTimeout(loop, 1000);
}
anyButtonPressedChangeListener.addListener((buttonState) => {
console.log(`Any button pressed changed to ${buttonState}`);
});
loop();

unable to change image source in DOM javascript

I’m Rahul and I’m new to coding. I have a query related to DOM event. Please look at the following code snippet -
let door1 = document.getElementById('one');
door1.src = "closed_door.svg";
const isClicked = (door) => {
if(door.src === "closed_door.svg") {
return true;
}
else {
return false;
}
};
door1.onclick = () => {
if(isClicked(door1)) {
door1.src = "beach.svg";}
};
To give you brief, one is an id for an element. Without isClicked, I am able to successfully change the src from closed door to beach on clicking. But when I introduce isClick, it doesn’t change. Can someone please tell me what I’m missing. I’ll be very thankful
Note - I'm building game similar to this - https://s3.amazonaws.com/codecademy-content/projects/chore-door/chore-door-final/index.html They are using the same process as mine. So please suggest a solution that tells me about the error I'm making here rather than an alternative to the problem
Regards
Rahul
As reported here:
the src reflected property will be the resolved URL — that is, the absolute URL that that turns into. So if that were on the page http://www.example.com, document.getElementById("foo").src would give you "http://www.example.com/images/example.png".
so to get the real src attribute you should use .getAttribute('src') like so:
const isClicked = (door) => {
if(door.getAttribute('src') === "closed_door.svg") {
return true;
}
else {
return false;
}
};
Ans also BTW, you can just shortcut it to:
const isClicked = (door) => door.getAttribute('src') === "closed_door.svg";
Replace your code with this..
let door1 = document.getElementById('one');
door1.src = "https://s3.amazonaws.com/codecademy-content/projects/chore-door/images/closed_door.svg";
const isClicked = (door1) => {
if(door1.src === "https://s3.amazonaws.com/codecademy-content/projects/chore-door/images/closed_door.svg") {
return true;
}
else {
return false;
}
};
door1.onclick = () => {
if(isClicked(door1)) {
door1.src = "https://s3.amazonaws.com/codecademy-content/projects/chore-door/images/beach.svg";}
};

Categories

Resources