Card flip with Onclick with cards randered from Json Data - javascript

I have a json list which i used to populate a list of react cards which has two sides. I want to flip to the info side when a button is clicked but I can only get it to flip all the cards. i can achieve it with hover within the css then only card hovered over flips.
Below is my card code
<MDBRow class="row">
<ul className="ulWidth">
<li className="liWidth"> {this.state.infos.map(post => {
return (
<div key={post.id} id="menu">
<MDBCol lg="4" className=" mb-3 column flip-card" id="myEltId">
<MDBCard className="card colCardinfoHeightImg flip-card-inner">
<img className="img-fluid infoImage" src={require('../../images/infoImage.png')} />
<MDBCardBody>
<MDBCardTitle className="CardTitle text-uppercase text-bold">{post.infoName}</MDBCardTitle>
<MDBCardText>
<strong>Data Example 1:</strong> {post.jsonData1}<br/>
<strong>Data Example 2:</strong> {post.jsonData2}<br/>
<strong>Data Example 3:</strong> {post.jsonData3}<br/>
</MDBCardText>
</MDBCardBody>
<div class="flip-card-back">
<MDBCard className=" colCardinfoHeight">
<MDBCardBody>
<MDBCardTitle>{post.infoName}</MDBCardTitle>
<MDBCardText>
<p><strong>Data Example 3:</strong>{post.jsonData4}<br/>
<strong>Data Example 3:</strong> {post.jsonData5}
</p>
<hr/>
<p><strong>Data Example 3:</strong> {post.jsonData6}<br/>
</MDBCardText>
<MDBBtn className="infoButton" color="orange" size="lg" onClick={this.clickFlipFunction}>Switch Today</MDBBtn>
</MDBCardBody>
</MDBCard>
</div>
</MDBCard>
</MDBCol>
</div>
);
})}
</li>
</ul>
</MDBRow>
this is the flip function i have attempted at the minute but it flips all the cards that are rendered.
onFlipCard(){
$(document).ready(function(){
$(".flip-card").css("transform", " rotateY(180deg)");
$(".flip-card-inner").css("transform", " rotateY(180deg)");
});
}
it works when i input the above css in the css file with the hover tag used and when the user hovers over it flips only the right card but i need it to be a clickable function.

Create an onClick event handler for the card.
Track whether the card is flipped or not within the component using something like an isCardFlipped state variable.
Whenever onClick is called, toggle the isCardFlipped state value.
Assign dynamic classnames using classnames , to show either flip-card or flip-card-inner based on the value in isCardFlipped
Edit : I would advice against the use of jquery, or to perform direct DOM manipulation. The major benefit of using React, which you might have missed out on, is to be able to update the DOM indirectly via React's internal working : in a gist, this allows only those DOM elements to be updated which have changed versus redrawing the entire DOM altogether. Read more on this here

Right now your event handler is getting all elements with the class "flip-card". That's what this part is doing:
$(".flip-card")
What you'll need instead is to use a reference to the specific element that was clicked, something like:
$(".flip-card").click(function(e){
$(e.target).css("transform", " rotateY(180deg)");
});
Event.target reference

Related

What is the best way to simulate a 'mouseleave' effect while the mouse is still on the element?

I have a list of elements, representing posts, and each one has two child elements. The first child-element is the post itself, the second is two arrows for moving the element up and down. The second child-element only appears when I hover over the parent container. The issue I have is that when I click on an arrow, up or down, I want to make the child-element disappear, but it doesn't, because I am still inside the parent.
The appearance of the second child-element is dependent on a Boolean variable (showArrows) which I toggle, so I tried to make that Boolean false, but the child-element still doesn't disappear, because the mouse is still inside.
I tried to dispatch a mouseleave event on click, but again it doesn't work because the mouse is still inside.
I tried to dispatch a mouseleave event AND add a class that adds no pointer events, and that's the only thing that works, HOWEVER, the issue now is that I have to find a way to remove the pointer-events otherwise hovering over the parent doesn't show the element again. Adding extra code to remove the pointer-events just makes the code too convoluted in my opinion, so I was wondering if there isn't a better way to do what I want.
Here's my current code:
handleMovePost(e) {
this.showPostIndex = parseInt(e.target.firstElementChild.firstElementChild.innerHTML) -1
this.showArrows = !this.showArrows
},
toggleShowArrows() {
const movedElement = document.querySelector('.movePost').closest('.post-wrapper')
movedElement.classList.toggle('remove')
let evt = new MouseEvent("mouseleave");
movedElement.dispatchEvent(evt);
}
<template>
<div class="container">
<div class="list-wrapper">
<div v-for="(post, index) in posts" :key="index" class="post-wrapper" #mouseenter="handleMovePost" #mouseleave="handleMovePost">
<div class="post">
<span class="post__index">{{index+1}}</span>
<span>{{post}}</span>
<span class="close__icon" #click="$emit('removePost', index)">X</span>
</div>
<div class="movePost" v-if="showArrows && showPostIndex === index">
<span #click="$emit('movePost', index, 'up'); toggleShowArrows()">
<font-awesome-icon icon="fa-solid fa-arrow-up" />
</span>
<span #click="$emit('movePost', index, 'down')">
<font-awesome-icon icon="fa-solid fa-arrow-down" />
</span>
</div>
</div>
</div>
</div>
</template>
I'm doing this on Vue, but I don't think that matters.

moving "pieces" around the board with React

I am working on a "simple" board game using create-react-app. I have a board component built from tiles. I want to move a pawn around the board in correlation to each player's position (position is saved and updated in each player's state object). Each of the tiles has a unique id. Essentially I want to make some kind of comparison check between the userPosition and the tile id, and render the user's pawn on the matching tile.
I had thought of 2 ways to do this, both cumbersome, and neither quite working:
Have a pawn for each user set inside each tile, and use CSS to toggle if they are visible or not.
Run a switch checking the userPosition, and updating the CSS position property accordingly.
The problem with both those methods is that I need to be able to target DOM elements: in the 1st option I need to be able to check the id of each tile. In the 2nd option I need to be able to target the element in order to change it's CSS position. using "regular" DOM methods like:
let myElement = document.getElementById("p1");
doesn't work.
I've been reading that maybe I need to be using a ref method, however I am using functional react with hooks, so I don't have a class to extend this to?
Also, I am totally open to other ideas as to how to visually move a pawn around the board, those are just 2 ideas I came up with.
some code:
the grid:
function Board () {
return (
<div className="boardContainer">
<div className="row">
<div className="col tile" id="1">
*
</div>
<div className="col tile" id="2" >
*
</div>
<div className="col tile" id="3">
*
</div>
<div className="col tile" id="4">
*
</div>
<div className="col tile" id="5">
*
</div>
</div>
the pawns:
<span className="dot1" id="p1"></span>
<span className="dot2"></span>
<span className="dot3"></span>
<span className="dot4"></span>
There are a lot of ways to do this without direct DOM manipulation.
You could have a <Tile> component that accepts a boolean value if a player is there and renders differently if one is:
function Tile({hasPlayer}) {
return <div className="col tile">
{hasPlayer ? <span className="dot1" /> : '*'}
</div>
}
Now you could create a row like:
<div className="row">
{[1,2,3,4,5].map(tileId => (
<Tile hasPlayer={tileId === playerPosition} />
)}
</div>
Or you could set the inline styles of the pawn to position the pawn absolutely.
CSS:
.pawn {
position: absolute;
transition: left 0.5s, top 0.5s; // smoothly move the pawn
}
If you knew the tiles were a certain size, say 50 pixels, you could then:
<span
className="pawn"
style={{
left: playerPosition.x * 50,
top: playerPosition.y * 50,
}}
/>
In general, think about your game as pure data. You just have state that allows you to know about what happening in the game. Then you simply render that state.
Don't think of moving things on the web page. Simply update the data that records the game state, and then just render it.

Vuetify on mouseover toggle data object within array list

I am trying to implement a functionality which will toggle a Vuetify badge element for each array object within the list when the containing div has been hovered over.
I am able to create a hover like functionality within v-for array list using css, which is fairly simple but how can I achieve similar outcome using vuetify components? As I have not found any questions discussing this or demonstrating it, assume it is possible. Have looked into
First Link
Second Link
Second Link
And much more but have not found similar enough example of what I desire.
I have added codepen example of what I currently have.
The badge should only appear on the element which is currently being hovered, however all badge elements are being executed when any element has been hovered on.
CodePen Link
Maybe I have missed out something obvious
HTML Part
<template>
<div id="app">
<div>My favourite fruit is <b>{{favouriteFruit}}</b></div> <br>
<div v-for="(dataArray, index) in testArray" #click="setCurrent(dataArray.name)">
<v-badge
:color="computedFavFruitColor[index]"
right
v-model="show"
>
<template v-slot:badge>
<span><v-icon color="#ECF0FF">{{computedFavFruit[index]}}</v-icon></span>
</template>
<div #mouseover="show = true" #mouseleave="show = false">
{{dataArray.name}}
</div>
</v-badge>
</div>
</div>
</template>
Any further help of suggestions regarding this manner would be appreciated.
While the show property is global, it counts for every item you hover. You want to target the element you hover. So I suggest to keep track of the index of the item you hover like this: https://codepen.io/reijnemans/pen/JjPrayp?editors=1010
<div v-for="(dataArray, index) in testArray" #click="setCurrent(dataArray.name)">
<v-badge
:color="computedFavFruitColor[index]"
right
>
<template v-slot:badge>
<span v-if="show == index"><v-icon color="#ECF0FF">{{computedFavFruit[index]}}</v-icon></span>
</template>
<div #mouseenter="show = index" #mouseleave="show = null">
{{dataArray.name}}
</div>
</v-badge>
</div>
And show = null ipv show = true in data block of vue

Can :key be used without v-for in VUE?

I use VUE to animate the slider (a list of images). There are two/three slides, each consisting of maximum 9 images. I have a computed property slide which returns the current set of images. There is another property start which stands for the number of the first image of a current slide. The <ul> element is the direct descendant of <transition>, so the animation takes place only when <ul>'s key changes. The first snippet is how I'd like to solve the problem. It is working but I can't find any information if it's ok to use a :key property on an element without v-for loop.
How was the problem solved untill now? There were two v-for loops. Slide computed property returned [slide] and artificially looped through one-element table. The problem I see is that v-for loops take the whole objects as :key and it's not what documentation recommends. The second snippet is how the code looks right now.
<transition>
<ul :key="this.start">
<li v-for="image in slide" :key="image.id">
<a>
<img />
</a>
</li>
</ul>
</transition>
<!--CURRENTLY-->
<transition>
<ul v-for="slide in slides" :key="slide">
<li v-for="image in slide" :key="image">
<a>
<img />
</a>
</li>
</ul>
</transition>
Since you asked me to post my comment as answer, I will simply quote the documentation: https://v2.vuejs.org/v2/api/#key
It can also be used to force replacement of an element/component
instead of reusing it. This can be useful when you want to:
Properly trigger lifecycle hooks of a component
Trigger transitions
For example:
<transition>
<span :key="text">{{ text }}</span>
</transition>
When text changes, the <span> will always be replaced instead of
patched, so a transition will be triggered.

Vue.js: Use aria-controls with conditional rendering in vue.js

I have two buttons which toggle some additional information on screen.
I added the buttons the aria-controls attribute und I render an id for the infobox.
Now I still get an error, when I validate the html, because I show this infobox only if a variable in the vuex store is true.
I render it with v-if.
So that means if the button was not clicked the element is not in the DOM and therefore the corresponding id is missing and I get an error.
I tried to use v-show because this would only hide it.
But this would still only render one infobox instead of 2.
Is the only way to get this right to make two infoboxes in the template and add the v-show to both? Or is there a nicer way to use aria-controls.
Thanks for any help
Best
m
Edit:
These are my buttons which have aria-controls.
<template>
<div>
<ul v-if="nav.items">
<li
v-for="(item, key) in nav.items"
#keyup.esc="closeInfoBox">
<button to="" aria-controls="item.name" aria-expanded="false">Designathon</button>
</li>
</ul>
</div>
</template>
And this is my infobox component:
<template>
<div class="Infobox" v-if="infoboxOpen" id="//should correspond to aria controls">
<span v-html="infoContent">Some content</span>
</div>
</template>
Which is only shown if infoboxOpen === true (from vuex store) and the content is replaced depending on which of the buttons is pressed.
(I left out some of this stuff, to make the code easier to understand and to focus on my question here).
This is where I could replace the v-if with the v-show but that would still render only one content. And I would like to have it as dynamic as possible, because users can add more infoboxes in the backend...
Hope this helps understanding my issue.
You're almost there, just make aria-controls a dynamic attribute using
:aria-controls="infoboxOpen ? item.name : ''":
<template>
<div>
<ul v-if="nav.items">
<li
v-for="(item, key) in nav.items"
#keyup.esc="closeInfoBox">
<button to="" :aria-controls="infoboxOpen ? item.name : ''" aria-expanded="false">Designathon</button>
</li>
</ul>
</div>
</template>

Categories