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.
Related
I need to do animation where my grid items will shrink and expand on scroll like this https://k72.ca/en/work, I have no progress for now can anyone give me a hint or help me to find package for it or tell me how should it be done?
my list looks like this:
{projects?.map((projectData) => {
const project = tData(projectData)
const slug = toKebabCase(
`${projectData.en?.client}-${projectData.en?.project}`
)
return (
<div
key={projectData.id}
className="projects-grid-item"
style={{
backgroundImage: `url(${project?.mainImage})`,
}}
>
<div className="project-content-wrapper">
<div className="nox-h-4">
<Link href={`${PROJECTS_ROUTE}/${slug}`}>
<a>
{project?.client} - {project?.project}
</a>
</Link>
</div>
<div className="additional-info-wrapper nox-body-1">
<div className="services">
{project?.services}
</div>
<div>{project?.year}</div>
</div>
</div>
</div>
)}
A good first step would be looking into the Intersection Observer API. It provides a way to track elements captured in the current viewport.
A second step might be to use an observer instance to change classes of elements in order to trigger the transitions that you're looking to implement.
Here's the challenge:
We have an image and text that describes it all nested in the same div. When somebody clicks on that image, we want Google Tag Manager to return that text.
Basically, we need to:
Go up 3 parent nodes
Go down to the child node with the class "right-section"
Go down to the child node with the class "is-title"
Go down to the child node with the tag "a"
Extract the text
Using my crude, self-taught Javascript knowledge, I came up with this monstrosity of a function:
function(){
return el.parentNode.parentNode.parentNode.getElementsByClassName("right-section")[0].getElementsByClassName("is-title")[0].getElementsByTagName("a")[0].innerText;
}
... which does not work at all.
Any suggestions?
this is an example markup basically the idea is use closest function to go up to dom tree and then use 'querySelector' with that result to go down to any element inside it
const img = document.querySelector(".item_image")
img.onclick = e=>{
const root = e.target.closest(".card") // goind back to root element
const text = root.querySelector(".text") // going down to text element
console.log(text.innerText)
}
<div class="card" style="width:400px;">
<div class="sections">
<img class="item_image" style="width:200px;" src="https://2.img-dpreview.com/files/p/E~C1000x0S4000x4000T1200x1200~articles/3925134721/0266554465.jpeg" alt="">
</div>
<div class="another-seci">
<div class="text">
Despite its modest MSRP, Fujifilm's entry-level X-A3 has dual control dials, a tilting touchscreen, and the same 24MP sensor from the company's flagship models - but with a traditional Bayer color filter array instead of X-Trans. We're pushing through our full
</div>
</div>
</div>
ok, Here is an example of what you need to do. If you need an exact answer that would work by just copy pasting it, show the block of HTML exactly how Amir did. I used Amir's HTML example to do the trick. Also, if you see that it takes the same text when you click on different images, include in your html snippet the code from other images, so that we could see how they relate to the parents and could refine the selectors.
Here's your CJS code:
function(){
return {{Click Element}}.parentElement.parentElement.querySelector(".text").innerText
}
There's completely no need to hop from node to node once you got to the bottom. From the bottom parentElement, just do the query selector once and you're in good hands.
You could also use .closest, but it seems like its complexity is way over just a few parentElements. I know it's insignificant, but hey.
Here is how I debug it right on this page (in Amir's answer):
document.querySelectorAll('.item_image').forEach(item => {
item.addEventListener('click', event => {
const root = event.target.closest(".card") // goind back to root element
const text = root.querySelector(".text") // going down to text element
console.log(text.innerText)
})
})
<div class="card" style="width:400px;">
<div class="sections">
<img class="item_image" style="width:200px;" src="https://2.img-dpreview.com/files/p/E~C1000x0S4000x4000T1200x1200~articles/3925134721/0266554465.jpeg" alt="">
</div>
<div class="another-seci">
<div class="text">
Despite its modest MSRP, Fujifilm's entry-level X-A3 has dual control dials, a tilting touchscreen, and the same 24MP sensor from the company's flagship models - but with a traditional Bayer color filter array instead of X-Trans. We're pushing through our full
</div>
</div>
</div>
<div class="card" style="width:400px;">
<div class="sections">
<img class="item_image" style="width:200px;" src="https://2.img-dpreview.com/files/p/E~C1000x0S4000x4000T1200x1200~articles/3925134721/0266554465.jpeg" alt="">
</div>
<div class="another-section">
<div class="text">
BlaDespite its modest MSRP, Fujifilm's entry-level X-A3 has dual control dials, a tilting touchscreen, and the same 24MP sensor from the company's flagship models - but with a traditional Bayer color filter array instead of X-Trans. We're pushing through our full
</div>
</div>
</div>
Here's what ultimately worked, for anyone Googling this:
function (){
el = {{Click Element}};
var item = el.parentElement.parentElement.nextElementSibling.getElementsByTagName('a')[0].innerText;
return item;
}
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
so currently I'm trying to link a particular section of another component page to an img when the image is clicked. Right now i'm doing this in the below component which takes me to the questions/1234 page but not to the exact section of questions/1234
handleClick(event) {
this.props.history.push('/questions/1234')
}
render() {
<img src={"/public/123"} onClick= {event =>
this.handleImageClick(event)} className="thumbnail"/>
/>
}
The component I'm linking the image to when clicked is
render() {
<div id="one">
1
</div>
<div id="two">
2
</div>
<div id="three">
3
</div>
}
And i would like to make it so that when I click on the image in the first component, this would lead me to the div "two" of the other component. How can I do this? Please Remember this is React
If I understand your question correctly then this should be possible by specifying a hash in the path of the page being navigated to.
If the hash value matches an element id in the page being navigated to, then the browser should automatically scroll to that element to ensure it's visible:
handleClick(event) {
/*
Adding #two causes browser to ensure that <div id="two"> is visible
after navigation
*/
this.props.history.push('/questions/1234#two');
}
Your sample code looks like reactjs, hence this answer.
What I have had to do in the past is use a library called react-scrollable-anchor. Check it out here
So in your case it'll probably look like so:
import ScrollableAnchor from 'react-scrollable-anchor';
// your other stuffs...
render() {
return <ScrollableAnchor id="one">
<div>
1
</div>
</ScrollableAnchor>
<ScrollableAnchor id="two">
<div>
2
</div>
</ScrollableAnchor>
<ScrollableAnchor id="three">
<div>
3
</div>
</ScrollableAnchor>
}
Note: Your render methods look weird without a return.
In the off chance that you're using Angular, which I know very little about, you may check out this library https://www.npmjs.com/package/ng2-page-scroll
Also, for something as simple as this you'll expect not to use another library on top of the routing library you already have, guess it's not that simple. Check these github thread out for:
react (using react-router) => https://github.com/ReactTraining/react-router/issues/394#issuecomment-220221604
angular => https://github.com/angular/angular/issues/14245
Additionally, your use of onClick event on the img tag feels a bit forced (js overuse) to me. Is there reason you're not doing as #John Rudell suggested in his/her/their comment? i.e wrapping your image in an anchor like so:
handleClick(event) {
// Not today we're just going to use an anchor for this
// this.props.history.push('/questions/1234')
}
render() {
<a href="/questions/1234#two >
<img src={"/public/123"} className="thumbnail" />
</a>
}
Note: technically, there's talk that the above is not a valid html as per dom nesting rules. Nesting an inline-block inside of an inline element. There are solutions to this. I just didn't want to distract from the simple idea of using the right tools.
In case OP or anyone else is wondering, a solution might be:
render() {
<div style={{ position: "relative" }} >
<img src={"/public/123"} className="thumbnail" />
<a
href="/questions/1234#two
style={{
position: "absolute",
top: 0,
bottom: 0,
width: "100%"
/>
</div>
}
Hope it helps.
I have a window scroll function that I am trying to dial in. Initially, I attempted to do with with waypoints, but couldn't figure it out.
My issue is my function is firing too early and not in the location I am wanting it to. It fires when the bottom of the screen gets to the main container this is all in, #home-info. The issue I have with this is if someone is scrolling slow they never see the animation. Ideally I want the function to fire when it gets to #info-container, the container with the animated objects in it.
What am I doing wrong that is not allowing this to happen or is there a better way to do this?
Fiddle. See it here
function boxes() {
window.addEventListener("scroll", function(event) {
var top, green, yellow, red;
top = this.scrollY;
green = document.querySelector("#green"),
yellow = document.querySelector("#yellow"),
red = document.querySelector("#red");
if(top > 100){
green.classList.add("green", "active");
yellow.classList.add("yellow", "active");
red.classList.add("red", "active");
}
}, false);
}
setTimeout(boxes,1200);
<div id="home-info">
<div id="home-info-container">
<div id="home-info-container">
<div id="home-info-container-description">
<div id="home-info-container-title">THE <span class="yellow-color sansbold">LEADING</span> PROVIDER FOR<br> DEMOLITION & WRECKING</div>
<div id="home-info-description">The Eslich Wrecking Company has been recognized as a leader in the demolition field for over 60 years. Over that time span, we have gained both the experience and reputation for doing quality work in an expedient, safe and cost efficient manner. Our track record proves that Eslich Wrecking has the people, equipment and know-how to tackle even the most complex situations and the most demanding jobs. This includes the capability to bond any project when necessary and to carry general liability, auto, and pollution insurance up to 10 million.</div>
</div>
</div>
<section id="info">
<article id="info-container">
<a href="projects">
<div id="green" class="box">
<div class="info-box-title">PROJECT AFTER PROJECT</div>
<div class="info-box-description">Over 60 years of accumulated projects.</div>
</div>
</a>
<a href="about">
<div id="yellow" class="box">
<div class="info-box-title">YEARS OF DEMOLITION HISTORY</div>
<div class="info-box-description">Find out about - The Eslich Wrecking Company.</div>
</div>
</a>
<a href="contact">
<div id="red" class="box">
<div class="info-box-title">GET IN TOUCH WITH US</div>
<div class="info-box-description">Contact us for more information.</div>
</div>
</a>
</article>
</section>
</div>
You don't want to hardcode your scrollbar position. The position at which your object becomes visible depends on the height of the view port, but more importantly on how much content you have above the target element.
Define a variable target with something like the following:
var target = $('#info-container').offset().top;
if(top >= target) {
// start animation
}
Also note that scroll top tells you nothing of what is in your view port, if you don't also look at the height of your window. In the condition above, use something like
var top = this.scrollY + $(window).height();
This will give you a condition that evaluates to true as soon as #info-container is scrolled into view. Depending on your needs, you may want your target to also include the $('#info-container').height(), if you want the scroll to start once the entire #info-container is in view.