Nested web-components and event handling












3















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)
})
}









share|improve this question

























  • 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
















3















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)
})
}









share|improve this question

























  • 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














3












3








3








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)
})
}









share|improve this question
















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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








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 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



















  • 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

















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












3 Answers
3






active

oldest

votes


















5














In the <memory-card>:




  • Create with CustomEvent() and dispatch a custom event with dispatchEvent()


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.






share|improve this answer
























  • 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 avoir bind(): () => this.flipCard()

    – Supersharp
    Jan 3 at 12:04



















1














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.






share|improve this answer
























  • 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



















1














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






share|improve this answer

























    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    5














    In the <memory-card>:




    • Create with CustomEvent() and dispatch a custom event with dispatchEvent()


    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.






    share|improve this answer
























    • 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 avoir bind(): () => this.flipCard()

      – Supersharp
      Jan 3 at 12:04
















    5














    In the <memory-card>:




    • Create with CustomEvent() and dispatch a custom event with dispatchEvent()


    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.






    share|improve this answer
























    • 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 avoir bind(): () => this.flipCard()

      – Supersharp
      Jan 3 at 12:04














    5












    5








    5







    In the <memory-card>:




    • Create with CustomEvent() and dispatch a custom event with dispatchEvent()


    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.






    share|improve this answer













    In the <memory-card>:




    • Create with CustomEvent() and dispatch a custom event with dispatchEvent()


    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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 avoir bind(): () => 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






    • 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 avoir bind(): () => 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













    1














    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.






    share|improve this answer
























    • 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
















    1














    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.






    share|improve this answer
























    • 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














    1












    1








    1







    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.






    share|improve this answer













    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.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    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



















    • 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











    1














    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






    share|improve this answer






























      1














      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






      share|improve this answer




























        1












        1








        1







        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






        share|improve this answer















        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







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 9 at 13:18

























        answered Jan 9 at 12:35









        Danny '365CSI' EngelmanDanny '365CSI' Engelman

        662512




        662512






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

            How to fix TextFormField cause rebuild widget in Flutter