Add div onclick using React, Gatsby, JavaScript - javascript

Hello I am trying to add another container div on click. With my current code I am getting the error TypeError: document.getElementById(...) is null. I got this answer from a stackoverflow problem and I beleive it does not work because I am using react/gatsby.
I have also tried having the <button id="click"></button> inside of trello.js and I am still getting the same error.
Trello.js:
import React from 'react'
import "./style.css"
import Button from "./Button"
export default function Trello() {
const draggables = document.querySelectorAll('.draggable')
const containers = document.querySelectorAll('.container')
draggables.forEach(draggable => {
draggable.addEventListener('dragstart', () => {
draggable.classList.add('dragging')
})
draggable.addEventListener('dragend', () => {
draggable.classList.remove('dragging')
})
})
containers.forEach(container => {
container.addEventListener('dragover', () => {
const draggable = document.querySelector('.dragging')
container.appendChild(draggable)
})
})
return (
<div>
<Button />
<div id="main-div">
<div>
<div class="draggable" draggable="true">Jason</div>
<div class="draggable" draggable="true">Jack</div>
<div class="draggable" draggable="true">Caleb</div>
<div class="draggable" draggable="true">Ransom</div>
</div>
<body>
<div class="container">
<p></p>
</div>
<div class="container">
<p></p>
</div>
<div class="container">
<p></p>
</div>
</body>
</div>
</div>
)
}
Button.js
import React from 'react'
export default function Button() {
document.getElementById('click').onclick = function () {
var div = document.createElement('div')
div.className = 'container'
document.getElementsByTagName('body')[0].appendChild(div)
}
return (
<div>
<button id="click"></button>
</div>
)
}

You are trying to access document (to get the identifier) on processing render time where it isn't defined yet, as Gatsby documentation explains:
Some of your code references “browser globals” like window or
document. If this is your problem you should see an error above like
“window is not defined”.
That's the reason why it is null in your snippet. If you want to do this, you may need to use a useEffect/componentDidMount hook/lifecycle. Both methods await and trigger once the component output has been rendered to the DOM.
However, accessing directly to the DOM in React is strongly not recommended, indeed, that's why you are using React, to create and manipulate a virtual DOM, where the cost (in terms of efficiency) for the browser to render and rerender it, instead of changing itself is the great value of React. Of course, it is not prohibited but you should avoid it. Instead, you can use useRef hook.
The last thing is that you may want to use something like this to achieve your goal is something like this:
import React from 'react'
export default function Button() {
const createDiv =()=> <div className={`container`}>'Im a new div</div>
return (
<div>
<button onClick={createDiv}>Click me</button>
</div>
)
}
The snippet above will create a new <div>, with "I'm a new div" inside as a return of your onClick function. If you need to create a new <div>on every click, the snippet needs a few changes, let me know and I'll update it.

import React from "react";
export default function Button() {
let click = () => {
var div = document.createElement("div");
div.innerHTML = "New DIV";
div.className = "container";
document.getElementsByTagName("body")[0].appendChild(div);
};
return (
<div>
<button onClick={click}>Click Me</button>
</div>
);
}

Related

Slider Carousel JS, React

Who can help with that? I need to write everything in a normal algorithm, not as it is now. I have three elements. I wanted to make it so that when I'm on the last one, the next button takes me back to the first element. To make a infinity loop carrousel. project at the react
import React, { useEffect, useState } from "react";
import { factory_img, factory_bg_svg } from "#/img_video";
export default function Factory_Video() {
const arr_items = []
useEffect(() => {
const items = document.querySelectorAll('.item')
for (let item of items) {
arr_items.push(item)
}
})
function next_slide() {
const el_1 = arr_items[0].classList.contains("active")
const el_2 = arr_items[1].classList.contains("active")
if (el_1) {
arr_items[0].classList.remove('active')
arr_items[0].classList.add("transform")
arr_items[1].classList.add("active")
arr_items[1].classList.remove('transform')
} else if (el_2) {
arr_items[1].classList.remove('active')
arr_items[1].classList.add("transform")
arr_items[2].classList.add("active")
arr_items[2].classList.remove('transform')
}
}
}
return (
<section className="section_factory" >
<img id="bg_section_factory" src={factory_bg_svg} alt="" />
<div className="container_factory">
<h1 className="h1_section_title" >О производстве <br /> Венарус</h1>
<div className="wrapper">
<div className="window">
<div className="item active" >
<img src={factory_img} />
</div>
<div className="item transform" >
<img src={factory_img} />
</div>
<div className="item transform" >
<img src={factory_img} />
</div>
</div>
<div className="navigation" >
<div className="btn_prev" ></div>
<div onClick={next_slide} className="btn_next" ></div>
</div>
</div>
</div>
</section>
);
}
I tried using array methods, but it didn't work.
The code is not complete so I can't tell the exact changes you need to do, however here are a list of recommendations I can see your code lacks or are wrong.
Never manipulate the dom directly with React.
Modifying the classlist in a function is not the React way and is prone to errors as it will be cleared if a re-render is triggered. Instead change the classes directly in the html.
It would be something like <div className={"item " +active===0?'active':''} >
You're declaring const arr_items = [] directly above useEffect. This varible will be cleared on every render. There are other ways to keep the info around in React, however in this case there is no point to keep the array of dom elements.
Your useEffect has no dependencies (the array sent as second parameter). This makes it execute every time the component runs so there's no point in using a useEffect here. (btw you don't need it at all for this to work)
Finally the "React" way (at least one approach) would be to have a state with the index of the active element. You just have to change this index with its corresponding setter and render the classes of each element conditionally.

TypeError: Cannot set property 'onscroll' of null

I am trying to create scroll effect, where on left side image is changing based on scroll position and right side is changing content while scrolling.
I am getting error when trying to execute onscroll javascript function. I am not sure what am I doing wrong - I am trying to use it in React App - is it possible that React has different rules to call this function?
I created example in here:
https://codesandbox.io/s/scroll-effect-37ecc
I would use useRef for dom manipulation in React. That will be optimized.
import { useLayoutEffect, useState, useRef, useEffect } from "react";
import { render } from "react-dom";
import classnames from "classnames";
import "./index.css";
const App = () => {
// const scrollingDiv = document.getElementById("scrollContainer");
// const img1 = document.getElementById("img1");
// const img2 = document.getElementById("img2");
const scrollingDiv = useRef(null);
const image1 = useRef(null);
const image2 = useRef(null);
useEffect(() => {
scrollingDiv.current.onscroll = function () {
if (scrollingDiv.scrollTop < 250) {
image1.current.src = "https://placeimg.com/250/100/arch";
image2.current.src = "https://placeimg.com/250/100/animals";
}
if (scrollingDiv.current.scrollTop > 500) {
image1.current.src = "https://placeimg.com/250/100/nature";
image2.current.src = "https://placeimg.com/250/100/people";
}
if (scrollingDiv.current.scrollTop > 1000) {
image1.current.src = "https://placeimg.com/250/100/tech";
image2.current.src = "https://placeimg.com/250/100/any";
}
};
}, []);
return (
<>
<div class="container">
<div class="left">
<img ref={image1} id="img1" src="https://placeimg.com/250/100/arch" />
<img ref={image2} id="img2" src="https://placeimg.com/250/100/animals" />
</div>
<div class="middle" ref={scrollingDiv} id="scrollContainer">
<div class="in-middle">
<div class="in-in-middle" id="1"></div>
<div class="in-in-middle" id="2"></div>
<div class="in-in-middle" id="3"></div>
</div>
</div>
</div>
</>
);
};
render(<App />, document.getElementById("root"));
Or you can put direct listeners on the element as well using onSroll prop.
Also, don't forget to clear all the listeners in useEffect cleaning function, that way, you won't have any memory leak issues.
normally, you would use useRef() function from react to get direct access to DOM elements, you can then use them like this:
const scrollingDiv = useRef(null);
<div id="scrollingSomething" ref={scrollingDiv}>
then you can manipulate the element using scollingDiv reference to the element
https://codesandbox.io/s/scroll-effect-forked-3nkbb

document.getElementById for iFrame in ReactJS

In my site, I need to send information (via postMessage) to an iFrame. I know in regular Javascript, I would accomplish this by using document.getElementById or $("#iframe") in JQuery to select the iframe. However, I am unsure of how to do this in ReactJS. Is there a specific way of doing this in ReactJS/NextJS that I just don't know about? I need access to the iframe (the child component) from its container (parent component).
If the iframe is rendered by React, and only the component that renders it (or its descendants) needs to access it, then typically you use refs.
If the iframe is always on the page, or rendered in some way outside of React, it's perfectly fine to get it via document.getElementById, document.querySelector, or other DOM methods.
Here's an example of using a ref in a functional component via useRef, but you can do the same thing (in a different way) in a class component via createRef. I'll use a div instead of an iframe, but it's the same for iframes.
function Example() {
const ref = React.useRef(null);
const doSomething = () => {
if (ref.current) {
console.log(`The div's text is "${ref.current.textContent}"`);
} else {
console.log("The div doesn't exist");
}
};
return (
<div>
<div ref={ref}>
This is the target div
</div>
<input type="button" onClick={doSomething} value="Click Me" />
</div>
);
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
Class component example:
class Example extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
this.doSomething = this.doSomething.bind(this);
}
doSomething() {
if (this.ref.current) {
console.log(`The div's text is "${this.ref.current.textContent}"`);
} else {
console.log("The div doesn't exist");
}
}
render() {
return (
<div>
<div ref={this.ref}>
This is the target div
</div>
<input type="button" onClick={this.doSomething} value="Click Me" />
</div>
);
}
}
ReactDOM.render(<Example/>, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>

How to change text in a <p> tag when hovering a button in ReactJs?

I'm trying to change the text of a <p> to David and Larry accordingly when each button (that has an image inside) has hovered. I have experimented with numerous things and found a way to change the CSS of the button with a function. But I was unable to find anything to change text since <p> is in a different class. Any suggestions to address this problem?
For your information, I have added a CSS color changing function I used earlier to the below code sample.
here's my code.
import React from 'react';
import "./Tri.css";
function Tri() {
function davidon(e) {
e.target.style.background = 'red';
}
function davidoff(e) {
e.target.style.background = 'green';
}
function larryon(e) {
e.target.style.background = 'red';
}
function larryoff(e) {
e.target.style.background = 'green';
}
return (
<div>
<div>
<div>
<button onMouseOver={davidon} onMouseLeave={davidoff}>
<img src={require(`./images/david.png`)} className="david"/>
</button>
<button onMouseOver={larryon} onMouseLeave={larryoff}>
<img src={require(`./images/larry.png`)} className="larry"/>
</button>
</div>
<div className="plex">
<p>Larry Or David?</p>
</div>
</div>
</div>
);
}
export default Tri;
Thanks in advance for you replies.
You need to think more in "React", and use component state and props. The offical documentation is a good place to start.
Here I've got two components.
1) Tri: which has it's own state, and builds the HTML using Button components
2) Button: since you need each button to change color depending on the mouse action it's best to separate that functionality out into a new component so that each instance can have its own state.
(I've intentionally left out the images in this example, but you could pass in a src prop to the button and have that handle the images too if you wanted.)
const { useState } = React;
// `Button` accepts a props object
// Here I've destructured out the button name,
// and the handleHover function
function Button({ name, handleHover }) {
// We initialise the state with "green"
const [ color, setColor ] = useState('green');
function handleColor() {
// We set the new color based on the current color
setColor(color => color === 'red' ? 'green' : 'red');
// And then call the `handleHover` function, passing in `name`
handleHover(name);
}
return (
<button
className={color}
onMouseOver={handleColor}
onMouseLeave={handleColor}
>
{name}
</button>
);
}
function Tri() {
// In `Tri` we set its own state for the name
// initialised to an empty string
const [ name, setName ] = useState('');
// A handler that changes the name
// This is the function we pass to each button
function handleHover(name) {
setName(name);
}
// Set up two buttons using our Button component
// assigning a name to each, and passing in our handler
// Whenever the name (state) is changed the name in the
// paragraph also changes
return (
<div>
<div>
<Button name="Larry" handleHover={handleHover} />
<Button name="David" handleHover={handleHover} />
</div>
<p>{name}</p>
</div>
);
}
ReactDOM.render(
<Tri />,
document.getElementById('react')
);
.red { background-color: red; }
.green { background-color: green; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Try using states. And don't change DOM-nodes dynamically in event handlers. Always use React functionality:
React uses a declarative form of programming (The Virtual DOM specifically). You define variables and set them and React updates the DOM if those change.
useState gives you the opportunity to declare an editable (through a setter function) variable. See Docs on State and Props.
import React from 'react';
import "./Tri.css";
function Tri(props) {
// props as immutable arguments (if needed)
// useState keeps an internal state in the component
let initialTxt = 'Larry Or David?';
const [text, setText] = React.useState(initialTxt);
return (
<div>
<div>
<div>
<button
className="david-btn"
onMouseOver={() => setText('David')}
onMouseLeave={() => setText(initialTxt)}>
<img src={require(`./images/david.png`)} className="david"/>
</button>
<button
className="larry-btn"
onMouseOver={() => setText('Larry')}
onMouseLeave={() => setText(initialTxt)}>>
<img src={require(`./images/larry.png`)} className="larry"/>
</button>
</div>
<div className="plex">
<p>{text}</p>
</div>
</div>
</div>
);
}
Also, extend ./Tri.css with the following code. You could use a style-variable but that would make your code more bloated and unreadable if you have access to CSS.
.david-btn,
.larry-btn {
background-color: green;
}
.david-btn:hover,
.larry-btn:hover {
background-color: red;
}
You are looking for Refs. You can read more about them in documentation.
I've created a simple example (based on your code).
Step by step what I did:
import useRef hook which is used to create reference.
import React, { useRef } from "react";
created reference:
const pTagRef = useRef();
passed reference to your p tag
<div ref={pTagRef} className="plex">
<p>Larry Or David?</p>
</div>
created function which can change the content of this reference where pTagRef.current is DOM element.
function setName(name) {
if (pTagRef.current) {
pTagRef.current.innerText = name;
}
}
called the function whenever name changed
setName("larry");
You should definitely use state for this but I hope this one helps you to get started.

React: Unexpected Behaviour

I want my code to toggle a person handler, Before it was working but since I split into components, It seem to have broken.
Toggle happens on button click (see inside return statement <
button className={btnClass}
onClick={props.toggler}>Button</button>
Here is my entire cockpit.js file (inside src/components/cockpit/cockpit.js).
import React from 'react';
import classes from './cockpit.css';
const Ccockpit = (props) => {
const assignedClasses = [];
let btnClass = ''
if (props.cocPersonState) {
btnClass = classes.red;
console.log(".......")
}
if (props.cocperson <= 2) {
assignedClasses.push(classes.red)
}
if (props.cocperson <= 1) {
assignedClasses.push(classes.bold)
}
return(
<div className={classes.cockpit}>
<h1> Hi I am react App</h1>
<p className={assignedClasses.join(' ')}>hey </p>
<button className={btnClass}
onClick={props.toggler}>Button</button>
</div>
);
}
export default Ccockpit;
and inside App.js
return (
<div className={classes.App}>
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
{person}
</div>
)
}
}
and this is my togglerpersonHandler code.
togglerPersonHandler = () => {
const doesShow = this.state.showPerson;
this.setState({
showPerson: !doesShow
});
}
I can't see to figure out that why it won't toggle and console.log/change color to red (It isn't changing the state). Can someone please review and figure out the mistake?
Your JSX still isn't right. Please review the JSX syntax with regards to giving it props/children.
You have this:
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
But those values aren't children, they're properties. So they need to be in the opening tag, like this:
<Ccockpit
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}/>
Revisit some React tutorials to see how JSX should be structured and how it works.

Categories

Resources