How to modify the code to not get appendchild error - javascript

I have been trying to use the below code but whenever I am trying to run it in react file I am getting this error
"Uncaught TypeError: Cannot read properties of null (reading 'appendChild')" error .
At first I found a design from codepen, then I tried to integrate it in react code but error is enevitable
Thus I am in need of finding answer to it
const container = document.getElementById('container');
const circlesArr = [];
for(let i=0; i<15; i++) {
circlesArr[i] = [];
for(let j=0; j<15; j++) {
const circle = document.createElement('div');
circle.classList.add('circle');
container.appendChild(circle);
circlesArr[i].push(circle);
}
}
function growCircles(i, j) {
if(circlesArr[i] && circlesArr[i][j]) {
if(!circlesArr[i][j].classList.contains('grow')) {
circlesArr[i][j].classList.add('grow');
setTimeout(() => {
growCircles(i-1, j)
growCircles(i+1, j)
}, 100)
setTimeout(() => {
circlesArr[i][j].classList.remove('grow');
}, 300);
}
}
}
circlesArr.forEach((15, i) => {
cols.forEach((circle, j) => {
circle.addEventListener('click', () => {
growCircles(i, j);
});
});
});
return (<div id="container" className="container"></div>)}
The CSS code
.container {
display: flex;
flex-wrap: wrap;
width: 450px;
}
.circle {
background-color: #5295F1;
height: 14px;
width: 14px;
transition: transform 0.3s linear;
}
.circle.grow {
transform: scale(2);
}

I assume the code before return is inside your component's body. It means you are trying to get an element with id "container" before rendering it so at that point the document.getElementById('container') returns null which doesn't have appendChild method.

Related

Javascript - How to make a section 'active' when scrolled to

I'm very new to JavaScript so apologies if I'm lacking clarity in any of my description.
I've built a small website that has a java-script generated menu and I'm just wanting to add functionality to highlight a section (and make 'active') in the menu when scrolled to. I've put together the following, which isn't throwing any errors in the console, so I'm unsure what I've missed:
const sectionHead = document.querySelectorAll('h2');
const sections = document.querySelectorAll('section');
const nav = document.querySelector('nav');
// build the nav
function navMenu(){
for(let section of sectionHead){
let listItem = document.createElement("li");
listItem.innerHTML = section.textContent;
nav.appendChild(listItem);
listItem.classList.add('menu__link');
listItem.addEventListener("click", ()=>{
section.scrollIntoView({behavior: "smooth"});
});
};
}
navMenu();
const nav_items = document.querySelectorAll('.menu__link')
window.addEventListener("scroll", () => {
let current = "";
sections.forEach((section) => {
const sectionTop = section.offsetTop;
const sectionHeight = section.clientHeight;
if (pageYOffset >= sectionTop - sectionHeight / 3) {
current = section.getAttribute("id");
}
});
//set section as active
nav_items.forEach((li) => {
li.classList.remove("your-active-class");
section.classList.remove("your-active-class");
if (section.classList.contains(current)) {
section.classList.add("your-active-class");
//console.log (li.classList);
}
});
});
The 'your-active-class' class has some custom CSS setup so it will just change visibility in the menu.
Any help is really appreciated
This is how you would observe whether a DOM Element is within view, and apply/remove a classname to it. If you look at your inspector, you'll see the 'active' class being added/removed when they enter and exit view.
window.addEventListener("load", function() {
//Query objects on document load
const sections = document.querySelectorAll("section")
console.log(`We have ${sections.length} sections`)
// Create options for Observer:
const options = {
rootMargin: '100px 0px',
threshold: [0.25, 0, 0.25, 0]
}
// Create instance of IO:
let observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
if (entry.intersectionRatio > 0) {
entry.target.classList.add('active')
} else {
entry.target.classList.remove('active')
}
})
}, options)
// Iterate over each queried el, and add observer:
sections.forEach(section => {
observer.observe(section)
})
})
section {
background-color: red;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 6rem auto;
transition: all 300ms ease-in-out;
}
.active {
background-color: rgba(0,255,0,0.3);
transition: all 300ms ease-in-out;
}
<section><div>This is the first section</div></section>
<section><div>This is the second</div></section>
<section><div>This is the third</div></section>

Trying to click div and fire off function in a class everytime it is clicked...says TypeError in console

I have been looking at how to use classes to create objects in Javascript and wanted to create a class with a function inside that I could use once a div is clicked...However, every time I click the div I get TypeError
I have google search answers and see results with a button in html solution, but I want to do this using javascript only.
I have tried using
document.getElementById("myDiv").onclick = function(event) {Bucket.selectAndFill(event)};
but it says that selectAndFill is not a function in the console.
let buckets = [];
class Bucket {
constructor(Content, SelectStatus) {
this.content = Content;
this.selectStatus = SelectStatus;
}
selectAndFill() {
for (let bucket of buckets) {
this.content = "i changed it";
}
}
}
for (let i = 0; i < 4; i++) {
buckets[i] = new Bucket("empty", false);
console.log(buckets[i]);
}
//this line was my attempt but it says that selectAndFill is not a function in the console
document.getElementById("myDiv").onclick = function() {
Bucket.selectAndFill()
};
#myDiv {
position: relative;
width: 100px;
height: 100px;
background: red;
}
<div id="myDiv">
</div>
You can call the function from Class modifying the call this way:
const bucketFill = new Bucket;
document.getElementById("myDiv").onclick = bucketFill.selectAndFill;
Just add static keyword next to the method.
let buckets = [];
class Bucket {
constructor(Content, SelectStatus) {
this.content = Content;
this.selectStatus = SelectStatus;
}
static selectAndFill() {
for (let bucket of buckets) {
this.content = "i changed it";
}
console.log(buckets);
}
}
for (let i = 0; i < 4; i++) {
buckets[i] = new Bucket("empty", false);
console.log(buckets[i]);
}
document.getElementById("myDiv").onclick = function() {
Bucket.selectAndFill();
};
#myDiv {
position: relative;
width: 100px;
height: 100px;
background: red;
}
<div id="myDiv">
</div>

React Portal input change removes elements

I've created this pen to demo it: https://codepen.io/no1melman/pen/WWyJqQ
essentially I have this portal:
const ChatArea = ({ children }) => {
const el = document.createElement("div");
el.classList.add('chatbox')
const root = document.getElementById("rootus");
useEffect(() => {
root.appendChild(el);
return () => root.removeChild(el);
}, []);
return createPortal(children, el);
};
And Use like:
const ChatBox = () => {
const [ reply, setReply ] = useState('');
const handleReply = (e) => {
e.preventDefault();
setReply(e.target.value);
}
return (
<ChatArea>
<div className="chat-title">Bot Convo</div>
<div className="chat-convo"></div>
<div className="chat-reply">
<input type="text" value={reply} onChange={handleReply} />
<button>Send</button>
</div>
</ChatArea>
)
}
For some reason as you start typing the body of the chatbox seems to disappear... I've put logs into the useEffect to see if that was causing it, but it didn't show what I thought
There are two issues here:
The first issue is
useEffect(() => {
root.appendChild(el);
return () => root.removeChild(el);
}, []);
Now as per the hooks principle the dependency should match the used variables inside the hook. If not used react will not run the effect next time.
SO in your case when you click on open chat it opens up the chat box. the effect ran and rendered the portal with the input box.
When you have typed the first letter and onChange happened
It triggered the rerender of ChatArea, which should have ideally run the effect again, but didn't run as dependency array is blank and react has not idea as to when re-render.so the effect ran once for the first time where chatArea ran UI mounted and next time, the effect did not run as dependency array is blank.
This line :
return createPortal(children, el); // is referring to the new el which is created
but not attached to DOM. Hence nothing is visible on UI inside chatbox.
Refer this link: do not miss dependencies React hooks FAQs sections they are great :)
2nd issue:
Ideally, new div should not be created every time. Persist the "div" element across consecutive rerenders
See this implementation: I know there can be other ways of implementing it.
Feedbacks are welcome.
const {
render,
createPortal
} = ReactDOM;
const {
useState,
useEffect,
useRef
} = React;
const ChatArea = ({
children
}) => {
const el = document.createElement("div");
el.classList.add('chatbox')
// This el above will be different in each render
// root will remain same, ideally root and chatdiv should be passed as props
const root = document.getElementById("rootus");
// this val and setVal is done to toggle render the chart area after
// chatDiv is updated
const [val, setVal] = useState(true)
const chatDiv = useRef(null)
// First useEffect to persist the div
useEffect(() => {
if (!chatDiv.current) {
chatDiv.current = el
setVal(!val)
}
}, [chatDiv])
useEffect(() => {
root.appendChild(chatDiv.current);
return () => {
return root.removeChild(chatDiv.current)
}; // you are removing it
}, [chatDiv, root]);
if (chatDiv.current) {
return createPortal(children, chatDiv.current)
}
return null
// In your case as the return happened first and found out the el
};
const ChatBox = () => {
const [reply, setReply] = useState('');
const handleReply = (e) => {
e.preventDefault();
setReply(e.target.value);
}
return ( <
ChatArea >
<
div className = "chat-title" > Bot Convo < /div>
<
div className = "chat-convo" > < /div>
<
div className = "chat-reply" >
<
input type = "text"
value = {
reply
}
onChange = {
handleReply
}
/> <
button > Send < /button> <
/div> <
/ChatArea>
)
}
const NavBar = ({}) => ( <
div className = "navbar" >
<
div > Home < /div> <
div > Somewhere < /div> <
/div>
);
const Main = () => {
const [showChat, setShowChat] = useState(false);
const openChat = () => {
setShowChat(true);
};
const chatterbox = showChat ? ( < ChatBox / > ) : null;
return ( <
div className = "container" >
<
h2 > Main < /h2> <
p >
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.The point of
using Lorem Ipsum is that it has a more - or - less normal distribution of
letters, as opposed to using 'Content here, content here', making it look like readable English.Many desktop publishing packages and web page editors now use Lorem Ipsum as their
default model text, and a search
for 'lorem ipsum'
will uncover many web sites still in their infancy.Various versions have evolved over the years, sometimes by accident, sometimes on purpose(injected humour and the like). <
/p> <
p style = {
{
display: "flex",
justifyContent: "center"
}
} >
<
button onClick = {
openChat
} > Open Chat < /button> <
/p> <
p style = {
{
display: "flex",
flexDirection: "column",
justifyContent: "center",
backgroundColor: "red"
}
} >
{
chatterbox
} < /p> <
/div>
);
};
const App = ({}) => ( <
div className = "app" >
<
NavBar / >
<
Main / >
<
/div>
);
render( < App / > , document.getElementById("rootus"));
body {
font-family: Raleway;
}
* {
box-sizing: border-box;
}
#rootus {
position: relative;
height: 100vh;
display: flex;
justify-content: center;
}
.navbar {
display: flex;
justify-content: center;
}
.navbar>div {
padding: 10px;
}
.navbar>div:hover {
background-color: gray;
cursor: pointer;
}
.container {
width: 960px;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
}
.chatbox {
width: 400px;
height: 200px;
position: absolute;
bottom: 0;
border: 2px solid black;
background: white;
display: flex;
flex-direction: column;
}
.chat-title {
background: black;
color: white;
}
.chat-convo {
flex: 1;
display: flex;
}
.chat-reply {
display: flex;
border-top: 1px solid black;
}
.chat-reply>input {
width: 80%;
padding: 8px;
border: none;
outline: none;
}
.chat-reply>button {
outline: none;
border: none;
flex: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="rootus">
</div>
Ui was not coming up proper in the stackoverflow code snippet, so I
had to edit somethings in styling. you can have a look at code pen
codepen linkaccording to your original styling

How to achieve this typing & deleting effect?

I’d like to replicate this website’s typing & deleting effect: http://www.cantercompanies.com.
I’ve been trying to figure this out using code blocks, HTML, CSS, and JS. However, I can’t seem to get this to work after hours and days of trying.
It’s obviously on my end, as I am fairly new to coding.
I really want this exact typing & deleting effect with blinking cursor. I of course will be using my own logo and fixed text, but need some guidance and help to replicate Canter's example above in the link provided… :-)
You don't need any library,
HTML
<div class="flex">
<p class="header-sub-title" id="word"></p><p class="header-sub-title blink">|</p>
</div>
CSS
#import 'https://fonts.googleapis.com/css?family=Roboto';
html, body {
background-color: #000;
}
h2, a, p {
color: #fff;
font-family: Roboto;
text-decoration: none;
}
p>a {
color: #fd084a;
}
.blink {
animation: blink 0.5s infinite;
}
#keyframes blink{
to { opacity: .0; }
}
.flex {
display: flex;
}
.header-sub-title {
color: #fff;
font-family: "Courier";
font-size: 20px;
padding: 0.1em;
}
JS
const words = ["CSS3.", "HTML5.", "javascript."];
let i = 0;
let timer;
function typingEffect() {
let word = words[i].split("");
var loopTyping = function() {
if (word.length > 0) {
document.getElementById('word').innerHTML += word.shift();
} else {
deletingEffect();
return false;
};
timer = setTimeout(loopTyping, 500);
};
loopTyping();
};
function deletingEffect() {
let word = words[i].split("");
var loopDeleting = function() {
if (word.length > 0) {
word.pop();
document.getElementById('word').innerHTML = word.join("");
} else {
if (words.length > (i + 1)) {
i++;
} else {
i = 0;
};
typingEffect();
return false;
};
timer = setTimeout(loopDeleting, 200);
};
loopDeleting();
};
typingEffect();
Reference:https://codepen.io/haaswill/pen/VKzXvZ

jquery fadeIn/Out, custom slideshow glitches, fade memory? fade queue?

I am building a background img slideshow and running into glitches I can't comprehend.
I have several objects that contains a list of images. I have two functions that will take these images, create one div per each, and add the imgs as background of these divs, all within a container.
Then, as described in this website, I fadeout the first div,and fadeIn the second, then move the first child to last child position, and loop, creating a slideshow effect.
When I want this over i .empty() the container. Then the process can start again with the same or another object.
The first time I do this, it works, but second, third... times, it starts to glitch. Not only two, but all divs start to fade in and out, for I don't know what reason
This happens even if I am using the same object in the first, second, third... attempts.
It would seem as if although the divs are erased from DOM, apparently there is some memory of them? Could it be related to the fact that created divs share the name with previously created divs? maybe fadein out keep some kind of internal queue I am unaware of?
Here is an JsFiddle:
https://jsfiddle.net/93h51k9m/11/
and the code:
$(document).ready(function(){
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/','http://lorempixel.com/400/200/sports/2/','http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/','http://lorempixel.com/400/200/sports/5/','http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {
};
function prepare(index) {
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('"+imgs[i]+"')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function(){
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function(){
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function(){
console.log("noImgObject called");
prepare(noImgObject);
});
});
Thank you
Every time click event is invoked, another interval is being started and that is the reason, actions are appended in the queue
Use global variable which will hold the setInterval instance and clear it every time you start new Interval.
var interval;
$(document).ready(function() {
var imgObject = {
imgs: ['http://lorempixel.com/400/200/sports/1/', 'http://lorempixel.com/400/200/sports/2/', 'http://lorempixel.com/400/200/sports/3/']
};
var imgObject2 = {
imgs: ['http://lorempixel.com/400/200/sports/4/', 'http://lorempixel.com/400/200/sports/5/', 'http://lorempixel.com/400/200/sports/6/']
};
var noImgObject = {};
function prepare(index) {
clearInterval(interval);
if ($("#cover").css("display") != "none") {
console.log("cover is visible: hide it first");
console.log("fadeOut cover in 3000ms");
$("#cover").fadeOut(3000, function() {
console.log("then empty cover")
$("#cover").empty();
console.log("now for the images")
roll(index);
});
} else {
console.log("cover is already hidden: now for the images");
roll(index);
};
};
function roll(index) {
if (typeof index.imgs != "undefined") {
console.log("called object has images")
console.log("get them and their numbers")
var imgs = index.imgs;
var imgsLength = imgs.length;
console.log("create as many divs as imgs, and place each img as bg in each div")
for (var i = 0; i < imgsLength; i++) {
$("#cover").append("<div class='imgdiv" + i + "'></div>");
$(".imgdiv" + i).css("background-image", "url('" + imgs[i] + "')");
};
console.log("now hide all but first div, fadeIn cover and start the carousel");
//as seen at http://snook.ca/archives/javascript/simplest-jquery-slideshow
$('#cover').fadeIn(3000);
$('#cover div:gt(0)').hide();
interval = setInterval(function() {
console.log("fade and swap")
$('#cover :first-child').fadeOut(3000)
.next('div').fadeIn(3000)
.end().appendTo('#cover')
}, 6000);
} else {
console.log("index has no images, nothing to do");
};
};
$("#imgobj").click(function() {
console.log("imgObject called");
prepare(imgObject);
});
$("#imgobj2").click(function() {
console.log("imgObject2 called");
prepare(imgObject2);
});
$("#noimgobj").click(function() {
console.log("noImgObject called");
prepare(noImgObject);
});
});
html {
color: black;
height: 100%;
padding: 0;
margin: 0;
overflow: hidden;
}
body {
height: 100%;
padding: 0;
margin: 0;
background: #f7fafa;
}
* {
box-sizing: border-box;
}
button {
cursor: pointer;
}
#buttons {
z-index: 1000;
}
#cover {
display: none;
position: fixed;
top: 5vh;
left: 0;
width: 100vw;
height: 95vh;
opacity: 0.5;
z-index: 0;
}
#cover div {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-repeat: no-repeat;
background-size: cover;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="buttons">
<button id="imgobj">imgObject</button>
<button id="imgobj2">imgObject2</button>
<button id="noimgobj">noImgObject</button>
</div>
<div id="cover"></div>

Categories