Nested web-components and event handling
I'm writing a memory game in javascript. I have made a web-component for the cards, <memory-card>
and a web-component to contain the cards and handle the game state <memory-game>
. The <memory-card>
class contains its image path for when its turned over, the default image to display as the back of the card, its turned state and an onclick
function to handle switching between the states and the images.
The <memory-game>
class has a setter that receives an array of images to generate <memory-cards>
from. What would be the best method to handle updating the game state in the <memory-game>
class? Should I attach an additional event listener to the <memory-card>
elements there or is there a better way to solve it? I would like the <memory-card>
elements to only handle their own functionality as they do now, ie changing images depending on state when clicked.
memory-game.js
class memoryGame extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
this.cards =
this.turnedCards = 0
}
flipCard () {
if (this.turnedCards < 2) {
this.turnedCards++
} else {
this.turnedCards = 0
this.cards.forEach(card => {
card.flipCard(true)
})
}
}
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.image = path
this.cards.push(card)
})
}
connectedCallback () {
this.cards.forEach(card => {
this.root.append(card)
})
}
}
customElements.define('memory-game', memoryGame)
memory-card.js
class memoryCard extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
// set default states
this.turned = false
this.path = 'image/0.png'
this.root.innerHTML = `<img src="${this.path}"/>`
this.img = this.root.querySelector('img')
}
set image (path) {
this.path = path
}
flipCard (turnToBack = false) {
if (this.turned || turnToBack) {
this.turned = false
this.img.setAttribute('src', 'image/0.png')
} else {
this.turned = true
this.img.setAttribute('src', this.path)
}
}
connectedCallback () {
this.addEventListener('click', this.flipCard())
}
}
customElements.define('memory-card', memoryCard)
implementing the custom event after Supersharp's answer
memory-card.js (extract)
connectedCallback () {
this.addEventListener('click', (e) => {
this.flipCard()
const event = new CustomEvent('flippedCard')
this.dispatchEvent(event)
})
}
memory-game.js (extract)
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.addEventListener('flippedCard', this.flipCard.bind(this))
card.image = path
this.cards.push(card)
})
}
javascript web-component dom-events custom-element
add a comment |
I'm writing a memory game in javascript. I have made a web-component for the cards, <memory-card>
and a web-component to contain the cards and handle the game state <memory-game>
. The <memory-card>
class contains its image path for when its turned over, the default image to display as the back of the card, its turned state and an onclick
function to handle switching between the states and the images.
The <memory-game>
class has a setter that receives an array of images to generate <memory-cards>
from. What would be the best method to handle updating the game state in the <memory-game>
class? Should I attach an additional event listener to the <memory-card>
elements there or is there a better way to solve it? I would like the <memory-card>
elements to only handle their own functionality as they do now, ie changing images depending on state when clicked.
memory-game.js
class memoryGame extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
this.cards =
this.turnedCards = 0
}
flipCard () {
if (this.turnedCards < 2) {
this.turnedCards++
} else {
this.turnedCards = 0
this.cards.forEach(card => {
card.flipCard(true)
})
}
}
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.image = path
this.cards.push(card)
})
}
connectedCallback () {
this.cards.forEach(card => {
this.root.append(card)
})
}
}
customElements.define('memory-game', memoryGame)
memory-card.js
class memoryCard extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
// set default states
this.turned = false
this.path = 'image/0.png'
this.root.innerHTML = `<img src="${this.path}"/>`
this.img = this.root.querySelector('img')
}
set image (path) {
this.path = path
}
flipCard (turnToBack = false) {
if (this.turned || turnToBack) {
this.turned = false
this.img.setAttribute('src', 'image/0.png')
} else {
this.turned = true
this.img.setAttribute('src', this.path)
}
}
connectedCallback () {
this.addEventListener('click', this.flipCard())
}
}
customElements.define('memory-card', memoryCard)
implementing the custom event after Supersharp's answer
memory-card.js (extract)
connectedCallback () {
this.addEventListener('click', (e) => {
this.flipCard()
const event = new CustomEvent('flippedCard')
this.dispatchEvent(event)
})
}
memory-game.js (extract)
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.addEventListener('flippedCard', this.flipCard.bind(this))
card.image = path
this.cards.push(card)
})
}
javascript web-component dom-events custom-element
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
1
I'd do something like this: create ahandleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have).bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.
– Dmitry
Jan 2 at 7:45
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47
add a comment |
I'm writing a memory game in javascript. I have made a web-component for the cards, <memory-card>
and a web-component to contain the cards and handle the game state <memory-game>
. The <memory-card>
class contains its image path for when its turned over, the default image to display as the back of the card, its turned state and an onclick
function to handle switching between the states and the images.
The <memory-game>
class has a setter that receives an array of images to generate <memory-cards>
from. What would be the best method to handle updating the game state in the <memory-game>
class? Should I attach an additional event listener to the <memory-card>
elements there or is there a better way to solve it? I would like the <memory-card>
elements to only handle their own functionality as they do now, ie changing images depending on state when clicked.
memory-game.js
class memoryGame extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
this.cards =
this.turnedCards = 0
}
flipCard () {
if (this.turnedCards < 2) {
this.turnedCards++
} else {
this.turnedCards = 0
this.cards.forEach(card => {
card.flipCard(true)
})
}
}
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.image = path
this.cards.push(card)
})
}
connectedCallback () {
this.cards.forEach(card => {
this.root.append(card)
})
}
}
customElements.define('memory-game', memoryGame)
memory-card.js
class memoryCard extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
// set default states
this.turned = false
this.path = 'image/0.png'
this.root.innerHTML = `<img src="${this.path}"/>`
this.img = this.root.querySelector('img')
}
set image (path) {
this.path = path
}
flipCard (turnToBack = false) {
if (this.turned || turnToBack) {
this.turned = false
this.img.setAttribute('src', 'image/0.png')
} else {
this.turned = true
this.img.setAttribute('src', this.path)
}
}
connectedCallback () {
this.addEventListener('click', this.flipCard())
}
}
customElements.define('memory-card', memoryCard)
implementing the custom event after Supersharp's answer
memory-card.js (extract)
connectedCallback () {
this.addEventListener('click', (e) => {
this.flipCard()
const event = new CustomEvent('flippedCard')
this.dispatchEvent(event)
})
}
memory-game.js (extract)
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.addEventListener('flippedCard', this.flipCard.bind(this))
card.image = path
this.cards.push(card)
})
}
javascript web-component dom-events custom-element
I'm writing a memory game in javascript. I have made a web-component for the cards, <memory-card>
and a web-component to contain the cards and handle the game state <memory-game>
. The <memory-card>
class contains its image path for when its turned over, the default image to display as the back of the card, its turned state and an onclick
function to handle switching between the states and the images.
The <memory-game>
class has a setter that receives an array of images to generate <memory-cards>
from. What would be the best method to handle updating the game state in the <memory-game>
class? Should I attach an additional event listener to the <memory-card>
elements there or is there a better way to solve it? I would like the <memory-card>
elements to only handle their own functionality as they do now, ie changing images depending on state when clicked.
memory-game.js
class memoryGame extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
this.cards =
this.turnedCards = 0
}
flipCard () {
if (this.turnedCards < 2) {
this.turnedCards++
} else {
this.turnedCards = 0
this.cards.forEach(card => {
card.flipCard(true)
})
}
}
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.image = path
this.cards.push(card)
})
}
connectedCallback () {
this.cards.forEach(card => {
this.root.append(card)
})
}
}
customElements.define('memory-game', memoryGame)
memory-card.js
class memoryCard extends HTMLElement {
constructor () {
super()
this.root = this.attachShadow({ mode: 'open' })
// set default states
this.turned = false
this.path = 'image/0.png'
this.root.innerHTML = `<img src="${this.path}"/>`
this.img = this.root.querySelector('img')
}
set image (path) {
this.path = path
}
flipCard (turnToBack = false) {
if (this.turned || turnToBack) {
this.turned = false
this.img.setAttribute('src', 'image/0.png')
} else {
this.turned = true
this.img.setAttribute('src', this.path)
}
}
connectedCallback () {
this.addEventListener('click', this.flipCard())
}
}
customElements.define('memory-card', memoryCard)
implementing the custom event after Supersharp's answer
memory-card.js (extract)
connectedCallback () {
this.addEventListener('click', (e) => {
this.flipCard()
const event = new CustomEvent('flippedCard')
this.dispatchEvent(event)
})
}
memory-game.js (extract)
set images (paths) {
paths.forEach(path => {
const card = document.createElement('memory-card')
card.addEventListener('flippedCard', this.flipCard.bind(this))
card.image = path
this.cards.push(card)
})
}
javascript web-component dom-events custom-element
javascript web-component dom-events custom-element
edited Jan 5 at 1:33


Supersharp
14.4k23171
14.4k23171
asked Jan 2 at 6:54
Christopher KarlssonChristopher Karlsson
186115
186115
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
1
I'd do something like this: create ahandleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have).bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.
– Dmitry
Jan 2 at 7:45
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47
add a comment |
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
1
I'd do something like this: create ahandleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have).bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.
– Dmitry
Jan 2 at 7:45
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
1
1
I'd do something like this: create a
handleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have). bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.– Dmitry
Jan 2 at 7:45
I'd do something like this: create a
handleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have). bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.– Dmitry
Jan 2 at 7:45
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47
add a comment |
3 Answers
3
active
oldest
votes
In the <memory-card>
:
- Create with
CustomEvent()
and dispatch a custom event withdispatchEvent()
In the <memory-game>
:
- Listen to your custom event with
addEventListener()
Because the cards are nested in the game, the event will bubble naturally to the container.
This way the 2 custom elements will stay loosley coupled.
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
You could also use the arrow notation to avoirbind()
:() => this.flipCard()
– Supersharp
Jan 3 at 12:04
add a comment |
It would be very helpful to see some of your existing code to know what you have tried. But without it you ca do what @Supersharp has proposed, or you can have the <memory-game>
class handle all events.
If you go this way then your code for <memory-card>
would listen for click
events on the entire field. It would check to see if you clicked on a card that is still face down and, if so, tell the card to flip. (Either through setting a property or an attribute, or through calling a function on the <memory-card>
element.)
All of the rest of the logic would exist in the <memory-game>
class to determine if the two selected cards are the same and assign points, etc.
If you want the cards to handle the click
event then you would have that code generate a new CustomEvent
to indicate that the card had flipped. Probably including the coordinates of the card within the grid and the type of card that is being flipped.
The <memory-game>
class would then listen for the flipped
event and act upon that information.
However you do this isn't really a problem. It is just how you want to code it and how tied together you want the code. If you never plan to use this code in any other games, then it does not matter as much.
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
add a comment |
Supersharps answer is not 100% correct.
click
events bubble up the DOM,
but CustomEvents do not
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
So you have to add the bubbles:true
yourself:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
more: https://javascript.info/dispatch-events
For an Eventbased programming challenge
this.cards.forEach(card => {
card.flipCard(true)
})
First of all that this.cards
is not required, as all cards are available in [...this.children]
!! Remember, in JavaScript Objects are passed by reference, so your this.cards
is pointing to the exact same DOM children
You have a dependency here,
the Game needs to know about the .flipCard
method in Card.
► Make your Memory Game send ONE Event which is received by EVERY card
hint: every card needs to 'listen' at Game DOM level to receive a bubbling Event
in my code that whole loop is:
game.emit('allCards','close');
Cards are responsible to listen for the correct EventListener
(attached to card.parentNode
)
That way it does not matter how many (or What ever) cards there are in your game
The DOM is your data-structure
If your Game no longer cares about how many or what DOM children it has,
and it doesn't do any bookkeeping of elements it already has,
shuffling becomes a piece of cake:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
My global rand function, producing a random value from an Array OR a number
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
Extra challenge
If you get your Event based programming right,
then creating a Memory Game with three matching cards is another piece of cake
.. or 4 ... or N matching cards
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54002383%2fnested-web-components-and-event-handling%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
In the <memory-card>
:
- Create with
CustomEvent()
and dispatch a custom event withdispatchEvent()
In the <memory-game>
:
- Listen to your custom event with
addEventListener()
Because the cards are nested in the game, the event will bubble naturally to the container.
This way the 2 custom elements will stay loosley coupled.
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
You could also use the arrow notation to avoirbind()
:() => this.flipCard()
– Supersharp
Jan 3 at 12:04
add a comment |
In the <memory-card>
:
- Create with
CustomEvent()
and dispatch a custom event withdispatchEvent()
In the <memory-game>
:
- Listen to your custom event with
addEventListener()
Because the cards are nested in the game, the event will bubble naturally to the container.
This way the 2 custom elements will stay loosley coupled.
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
You could also use the arrow notation to avoirbind()
:() => this.flipCard()
– Supersharp
Jan 3 at 12:04
add a comment |
In the <memory-card>
:
- Create with
CustomEvent()
and dispatch a custom event withdispatchEvent()
In the <memory-game>
:
- Listen to your custom event with
addEventListener()
Because the cards are nested in the game, the event will bubble naturally to the container.
This way the 2 custom elements will stay loosley coupled.
In the <memory-card>
:
- Create with
CustomEvent()
and dispatch a custom event withdispatchEvent()
In the <memory-game>
:
- Listen to your custom event with
addEventListener()
Because the cards are nested in the game, the event will bubble naturally to the container.
This way the 2 custom elements will stay loosley coupled.
answered Jan 2 at 13:07


SupersharpSupersharp
14.4k23171
14.4k23171
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
You could also use the arrow notation to avoirbind()
:() => this.flipCard()
– Supersharp
Jan 3 at 12:04
add a comment |
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
You could also use the arrow notation to avoirbind()
:() => this.flipCard()
– Supersharp
Jan 3 at 12:04
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
Thank you, I was able to implement the solution in this answer.
– Christopher Karlsson
Jan 2 at 22:43
1
1
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
My implementation: In memory-card.js i added const event = new CustomEvent('flippedCard') this.dispatchEvent(event) to the pre-existing click eventlistener in memory-game.js i added card.addEventListener('flippedCard', this.flipCard.bind(this)) to set images after creating the memory-card element
– Christopher Karlsson
Jan 2 at 22:51
1
1
You could also use the arrow notation to avoir
bind()
: () => this.flipCard()
– Supersharp
Jan 3 at 12:04
You could also use the arrow notation to avoir
bind()
: () => this.flipCard()
– Supersharp
Jan 3 at 12:04
add a comment |
It would be very helpful to see some of your existing code to know what you have tried. But without it you ca do what @Supersharp has proposed, or you can have the <memory-game>
class handle all events.
If you go this way then your code for <memory-card>
would listen for click
events on the entire field. It would check to see if you clicked on a card that is still face down and, if so, tell the card to flip. (Either through setting a property or an attribute, or through calling a function on the <memory-card>
element.)
All of the rest of the logic would exist in the <memory-game>
class to determine if the two selected cards are the same and assign points, etc.
If you want the cards to handle the click
event then you would have that code generate a new CustomEvent
to indicate that the card had flipped. Probably including the coordinates of the card within the grid and the type of card that is being flipped.
The <memory-game>
class would then listen for the flipped
event and act upon that information.
However you do this isn't really a problem. It is just how you want to code it and how tied together you want the code. If you never plan to use this code in any other games, then it does not matter as much.
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
add a comment |
It would be very helpful to see some of your existing code to know what you have tried. But without it you ca do what @Supersharp has proposed, or you can have the <memory-game>
class handle all events.
If you go this way then your code for <memory-card>
would listen for click
events on the entire field. It would check to see if you clicked on a card that is still face down and, if so, tell the card to flip. (Either through setting a property or an attribute, or through calling a function on the <memory-card>
element.)
All of the rest of the logic would exist in the <memory-game>
class to determine if the two selected cards are the same and assign points, etc.
If you want the cards to handle the click
event then you would have that code generate a new CustomEvent
to indicate that the card had flipped. Probably including the coordinates of the card within the grid and the type of card that is being flipped.
The <memory-game>
class would then listen for the flipped
event and act upon that information.
However you do this isn't really a problem. It is just how you want to code it and how tied together you want the code. If you never plan to use this code in any other games, then it does not matter as much.
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
add a comment |
It would be very helpful to see some of your existing code to know what you have tried. But without it you ca do what @Supersharp has proposed, or you can have the <memory-game>
class handle all events.
If you go this way then your code for <memory-card>
would listen for click
events on the entire field. It would check to see if you clicked on a card that is still face down and, if so, tell the card to flip. (Either through setting a property or an attribute, or through calling a function on the <memory-card>
element.)
All of the rest of the logic would exist in the <memory-game>
class to determine if the two selected cards are the same and assign points, etc.
If you want the cards to handle the click
event then you would have that code generate a new CustomEvent
to indicate that the card had flipped. Probably including the coordinates of the card within the grid and the type of card that is being flipped.
The <memory-game>
class would then listen for the flipped
event and act upon that information.
However you do this isn't really a problem. It is just how you want to code it and how tied together you want the code. If you never plan to use this code in any other games, then it does not matter as much.
It would be very helpful to see some of your existing code to know what you have tried. But without it you ca do what @Supersharp has proposed, or you can have the <memory-game>
class handle all events.
If you go this way then your code for <memory-card>
would listen for click
events on the entire field. It would check to see if you clicked on a card that is still face down and, if so, tell the card to flip. (Either through setting a property or an attribute, or through calling a function on the <memory-card>
element.)
All of the rest of the logic would exist in the <memory-game>
class to determine if the two selected cards are the same and assign points, etc.
If you want the cards to handle the click
event then you would have that code generate a new CustomEvent
to indicate that the card had flipped. Probably including the coordinates of the card within the grid and the type of card that is being flipped.
The <memory-game>
class would then listen for the flipped
event and act upon that information.
However you do this isn't really a problem. It is just how you want to code it and how tied together you want the code. If you never plan to use this code in any other games, then it does not matter as much.
answered Jan 2 at 17:14
IntervaliaIntervalia
4,70211134
4,70211134
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
add a comment |
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
Thank you for your answer Intervalia it further explains the logic suggested by @Supersharp and was very helpful. I added my code to my question in case you have a suggestion for a better implementation.
– Christopher Karlsson
Jan 2 at 22:56
add a comment |
Supersharps answer is not 100% correct.
click
events bubble up the DOM,
but CustomEvents do not
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
So you have to add the bubbles:true
yourself:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
more: https://javascript.info/dispatch-events
For an Eventbased programming challenge
this.cards.forEach(card => {
card.flipCard(true)
})
First of all that this.cards
is not required, as all cards are available in [...this.children]
!! Remember, in JavaScript Objects are passed by reference, so your this.cards
is pointing to the exact same DOM children
You have a dependency here,
the Game needs to know about the .flipCard
method in Card.
► Make your Memory Game send ONE Event which is received by EVERY card
hint: every card needs to 'listen' at Game DOM level to receive a bubbling Event
in my code that whole loop is:
game.emit('allCards','close');
Cards are responsible to listen for the correct EventListener
(attached to card.parentNode
)
That way it does not matter how many (or What ever) cards there are in your game
The DOM is your data-structure
If your Game no longer cares about how many or what DOM children it has,
and it doesn't do any bookkeeping of elements it already has,
shuffling becomes a piece of cake:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
My global rand function, producing a random value from an Array OR a number
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
Extra challenge
If you get your Event based programming right,
then creating a Memory Game with three matching cards is another piece of cake
.. or 4 ... or N matching cards
add a comment |
Supersharps answer is not 100% correct.
click
events bubble up the DOM,
but CustomEvents do not
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
So you have to add the bubbles:true
yourself:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
more: https://javascript.info/dispatch-events
For an Eventbased programming challenge
this.cards.forEach(card => {
card.flipCard(true)
})
First of all that this.cards
is not required, as all cards are available in [...this.children]
!! Remember, in JavaScript Objects are passed by reference, so your this.cards
is pointing to the exact same DOM children
You have a dependency here,
the Game needs to know about the .flipCard
method in Card.
► Make your Memory Game send ONE Event which is received by EVERY card
hint: every card needs to 'listen' at Game DOM level to receive a bubbling Event
in my code that whole loop is:
game.emit('allCards','close');
Cards are responsible to listen for the correct EventListener
(attached to card.parentNode
)
That way it does not matter how many (or What ever) cards there are in your game
The DOM is your data-structure
If your Game no longer cares about how many or what DOM children it has,
and it doesn't do any bookkeeping of elements it already has,
shuffling becomes a piece of cake:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
My global rand function, producing a random value from an Array OR a number
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
Extra challenge
If you get your Event based programming right,
then creating a Memory Game with three matching cards is another piece of cake
.. or 4 ... or N matching cards
add a comment |
Supersharps answer is not 100% correct.
click
events bubble up the DOM,
but CustomEvents do not
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
So you have to add the bubbles:true
yourself:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
more: https://javascript.info/dispatch-events
For an Eventbased programming challenge
this.cards.forEach(card => {
card.flipCard(true)
})
First of all that this.cards
is not required, as all cards are available in [...this.children]
!! Remember, in JavaScript Objects are passed by reference, so your this.cards
is pointing to the exact same DOM children
You have a dependency here,
the Game needs to know about the .flipCard
method in Card.
► Make your Memory Game send ONE Event which is received by EVERY card
hint: every card needs to 'listen' at Game DOM level to receive a bubbling Event
in my code that whole loop is:
game.emit('allCards','close');
Cards are responsible to listen for the correct EventListener
(attached to card.parentNode
)
That way it does not matter how many (or What ever) cards there are in your game
The DOM is your data-structure
If your Game no longer cares about how many or what DOM children it has,
and it doesn't do any bookkeeping of elements it already has,
shuffling becomes a piece of cake:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
My global rand function, producing a random value from an Array OR a number
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
Extra challenge
If you get your Event based programming right,
then creating a Memory Game with three matching cards is another piece of cake
.. or 4 ... or N matching cards
Supersharps answer is not 100% correct.
click
events bubble up the DOM,
but CustomEvents do not
Why firing a defined event with dispatchEvent doesn't obey the bubbling behavior of events?
So you have to add the bubbles:true
yourself:
[yoursender].dispatchEvent(new CustomEvent([youreventName], {
bubbles: true,
detail: [yourdata]
}));
more: https://javascript.info/dispatch-events
For an Eventbased programming challenge
this.cards.forEach(card => {
card.flipCard(true)
})
First of all that this.cards
is not required, as all cards are available in [...this.children]
!! Remember, in JavaScript Objects are passed by reference, so your this.cards
is pointing to the exact same DOM children
You have a dependency here,
the Game needs to know about the .flipCard
method in Card.
► Make your Memory Game send ONE Event which is received by EVERY card
hint: every card needs to 'listen' at Game DOM level to receive a bubbling Event
in my code that whole loop is:
game.emit('allCards','close');
Cards are responsible to listen for the correct EventListener
(attached to card.parentNode
)
That way it does not matter how many (or What ever) cards there are in your game
The DOM is your data-structure
If your Game no longer cares about how many or what DOM children it has,
and it doesn't do any bookkeeping of elements it already has,
shuffling becomes a piece of cake:
shuffle() {
console.log('► Shuffle DOM children');
let game = this,
cards = [...game.children],//create Array from a NodeList
idx = cards.length;
while (idx--) game.insertBefore(rand(cards), rand(cards));//swap 2 random DOM elements
}
My global rand function, producing a random value from an Array OR a number
rand = x => Array.isArray(x) ? x[rand(x.length)] : 0 | x * Math.random(),
Extra challenge
If you get your Event based programming right,
then creating a Memory Game with three matching cards is another piece of cake
.. or 4 ... or N matching cards
edited Jan 9 at 13:18
answered Jan 9 at 12:35
Danny '365CSI' EngelmanDanny '365CSI' Engelman
662512
662512
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54002383%2fnested-web-components-and-event-handling%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Yes and no. ....
– Jonas Wilms
Jan 2 at 6:58
This is broad, provide the codes you have tried.
– Aria
Jan 2 at 6:59
1
I'd do something like this: create a
handleCardFlip(cardNumber, isOpen)
method on the "game controller" (or whatever you have).bind
this method to the game controller. Pass this method ot cards when you create them. Call this method from inside cards.– Dmitry
Jan 2 at 7:45
@Aria I'm trying to make this a broad best practice question, I don't have access to my code at the moment but I'm not necessarily looking for a code answer either I want to know how I should think about handling events in nested web-components, the answer should be applicable to any scenario where you want a web component to respond to changes in one of its nested components.
– Christopher Karlsson
Jan 2 at 7:46
@Dmitry Thank you, that sounds sensible, I will try this approach when I get home
– Christopher Karlsson
Jan 2 at 7:47