It should be <div className="dropdown">
instead of <div class="dropdown">
and <div id={styles.myDropdown} className={styles.dropdownContent}>
You would also avoid doing vanilla js inside a react/next app.
You would instead have a react state like this:
const [dropdownToggled, toggleDropdown] = useState(false);
const handleClick = () => {
toggleDropdown(!dropdownToggled);
};
And have a condition on whether your jsx have a className hidden
that sets display: none
something like this:
<div
className={`${styles.dropdownContent}
${dropdownToggled ? styles.hidden : ""}`}
>
<a href="/">Link 1</a>
<a href="/">Link 2</a>
<a href="/">Link 3</a>
</div>
To make the dropdown close when the user clicks outside, you would have a div like this:
<div
className={styles.backdrop}
onClick={() => toggleDropdown(true)}
></div>
that is styled like this:
.backdrop {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: -1;
}
Now since this div takes the whole screen and is positioned absolute when the user clicks anywhere on the page the onClick will fire and toggle the dropdown.
Working CodeSandbox.
В процессе разработки веб-приложений неизбежно возникают ошибки, которые могут привести к неправильной работе программы. Одной из таких ошибок является ошибка типа Uncaught TypeError: Не удается прочитать свойства null, читающие classList. Эта ошибка возникает, когда программа пытается прочитать свойства объекта, который имеет значение null или undefined. Данная ошибка связана с методом classList, который используется для работы с классами элементов HTML.
Возникновение ошибки типа Uncaught TypeError может быть вызвано несколькими причинами. Одна из причин — это неправильное обращение к элементу DOM. Например, если программа пытается прочитать свойство classList у элемента, который не существует на странице, то возникнет ошибка. Также, если программа пытается обратиться к элементу DOM до его полной загрузки, то класс элемента может быть еще не доступен и возникнет ошибка.
Например, следующий код может вызвать ошибку типа Uncaught TypeError: Не удается прочитать свойства null, читающие classList:
var element = document.getElementById("my-element");
element.classList.add("active");
Чтобы избежать ошибки типа Uncaught TypeError: Не удается прочитать свойства null, читающие classList, необходимо проверять, существует ли элемент перед обращением к его свойствам. Например, можно использовать условную конструкцию if, чтобы проверить, что элемент не равен null или undefined:
var element = document.getElementById("my-element");
if (element) {
element.classList.add("active");
}
Также стоит обратить внимание на правильную загрузку элементов DOM. Для этого можно использовать события onload или DOMContentLoaded, чтобы убедиться, что элементы полностью загрузились перед их использованием.
Важно отметить, что ошибка типа Uncaught TypeError может возникать не только при работе с методом classList. Она может возникать также и при работе с другими свойствами объектов. Поэтому при разработке веб-приложений необходимо быть внимательным и аккуратным, чтобы избегать подобных ошибок и обеспечить бесперебойную работу программы.
Uncaught TypeError (неперехваченная ошибка типа) является одной из наиболее распространенных ошибок, с которыми разработчики сталкиваются при написании JavaScript кода.
Эта ошибка возникает, когда попытка прочитать свойство или вызвать метод у значения, которое имеет тип null или undefined. Например:
var obj = null;
var myValue = obj.name; // Uncaught TypeError: Cannot read property 'name' of null
В данном примере, переменная obj
имеет значение null
, поэтому при попытке прочитать свойство name
, возникает ошибка.
Чтобы избежать этой ошибки, необходимо проверить, что значение не является null
или undefined
, перед тем как пытаться использовать его:
var obj = null;
if (obj !== null) {
var myValue = obj.name;
}
Либо можно использовать оператор условного выполнения (?.
), который позволяет избежать ошибки при попытке обращения к свойствам или методам объекта, если значение равно null
или undefined
:
var obj = null;
var myValue = obj?.name; // не вызовет ошибку
Использование оператора условного выполнения позволяет более компактно написать код и избежать проверок на null
или undefined
вручную.
В заключение, ошибка Uncaught TypeError часто возникает при работе с JavaScript кодом, особенно при обращении к свойствам или методам объектов. Что бы избежать этой ошибки, необходимо аккуратно проверять значения на null
или undefined
перед использованием их свойств или методов.
Причина ошибки: Неудачное чтение свойств null
Во время работы с JavaScript вы можете столкнуться с ошибкой Uncaught TypeError: Не удается прочитать свойства null. Эта ошибка обычно возникает, когда вы пытаетесь получить доступ к свойству или методу объекта, которого нет.
Один из наиболее распространенных случаев возникновения этой ошибки — попытка доступа к свойству classList неопределенного значения. Свойство classList является свойством DOM-элемента и предоставляет доступ к его классам. Оно содержит методы для добавления, удаления и проверки наличия классов.
Однако, если попытаться получить доступ к свойству classList объекта со значением null или undefined, возникнет ошибка типа TypeError: Cannot read property ‘classList’ of null.
Вам может потребоваться подробнее исследовать ваш код, чтобы оценить почему значение null или undefined было передано или получено вместо ожидаемого объекта. Возможно, вы не правильно инициализировали переменные, вызвали функцию с некорректными аргументами, или сделали что-то еще неожиданное в вашем коде.
Есть несколько способов избежать этой ошибки при работе с объектами:
- Всегда проверяйте значения переменных на null или undefined перед попыткой получения доступа к их свойствам или методам:
if (myVariable !== null && myVariable !== undefined) {
// Выполняйте код, использующий myVariable
}
- Убедитесь, что вы корректно инициализируете переменные и объекты перед их использованием:
var myVariable = document.getElementById('myElement');
if (myVariable !== null) {
// Выполняйте код, использующий myVariable
}
- Используйте условные операторы, чтобы избежать проблем с неопределенными значениями:
var myVariable = null;
var myValue = myVariable
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.
Already on GitHub?
Sign in
to your account
Open
lekoala opened this issue
Oct 6, 2022
· 15 comments
Comments
Prerequisites
- I have searched for duplicate or closed issues
- I have validated any HTML to avoid common problems
- I have read the contributing guidelines
Describe the issue
I’m having the following issue in production on one website
Cannot read properties of null (reading ‘classList’)
At the lines
this._element.classList.add(CLASS_NAME_HIDE) // @deprecated | |
this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW) | |
EventHandler.trigger(this._element, EVENT_HIDDEN) |
My guess is that the element is already removed when that callback is called (i’m calling .dispose in the bs.hidden callback event I don’t know if that’s related or not. it should work without issues either way in my opinion).
Reduced test cases
My proposal is to simply add a null check before and return early if this._element is null
Demo
https://codepen.io/lekoalabe/pen/oNdPNXJ
click create modal
click create toast
accept sweet alert modal
try to click close icon on toast => it triggers the error
What operating system(s) are you seeing the problem on?
Windows
What browser(s) are you seeing the problem on?
Chrome
What version of Bootstrap are you using?
v5.2.2
Hello @lekoala. Bug reports must include a live demo of the issue. Per our contributing guidelines, please create a reduced test case on CodePen or StackBlitz and report back with your link, Bootstrap version, and specific browser and Operating System details.
ok so it’s kind of hard to replicate but here it goes
it can trigger a variety of errors I’ve listed in the codepen
i have to admit it’s a bit extreme to have sweetalert, modals and toasts all over the place, but it shouldn’t create js error anyway
you have to click around a fair bit to trigger the various error. the toast error is caused like this:
- click create modal
- click create toast
- accept sweet alert modal
- try to click close icon on toast => it triggers the error
ok so it’s kind of hard to replicate but here it goes
it can trigger a variety of errors I’ve listed in the codepen
i have to admit it’s a bit extreme to have sweetalert, modals and toasts all over the place, but it shouldn’t create js error anyway
you have to click around a fair bit to trigger the various error. the toast error is caused like this:
- click create modal
- click create toast
- accept sweet alert modal
- try to click close icon on toast => it triggers the error
This issue is related to #37245
i’m not sure adding null checks will fix the root cause, but at least it would prevent js errors. these errors don’t seem to prevent expected behaviour, so I think it should be pretty safe to add them.
I had a «quick» look to the given codepen.
-
You have imported the
bootstrap.bundle.js
twice, that was causingmodal.js:357 Uncaught TypeError: Cannot read properties of null (reading 'hide')
-
plus, I couldn’t spot any referral to
bootstrap.Toast
😖
Is a bit strange to find an error on bootstrap toast, without using it. 😵💫
@GeoSot sorry about that indeed i tried a couple of variations and indeed i left a duplicate import. without it i dont have the issue anymore (it still happens (without duplicates) in prod in another app but it’s hard to replicate the exact setup).
not sure why importing twice would break everything though?
I’m using a «toaster» helper method that calls new bootstrap.Toast under the hood, i can extract this helper if that makes anything clearer but in any case, without the duplicate import i didn’t find a way to replicate my issue
For sure, I can try to help only if you use the Bs components explicitly, and not inside another library. It is not possible to guess any side effects
@GeoSot so i managed to replicate it finally!
basically it’s very tricky to get it by hand, which is why it happens rarely
if you click or double click on the close icon right when it’s supposed to close, it may fail
I’ve simulated this with a setTimeout
I tried it a lot in comparison with a ‘native’ toast. On your codepen example, for some reason (not able to debug all these lines 😞), the second click of the double-click, seems, it is finding the event listener still registered, so it tries to execute the hide
process twice. In contradiction, this cannot be replicated on a native toast https://getbootstrap.com//docs/5.2/components/toasts/#live-example
So maybe the better scenario is to try to decouple the code a bit, or try to refactor based on the given events
@GeoSot ok fair point i’ve simplified the example even more and I think I found the actual issue:
- if you use autohide
- and you dispose of the instance on hidden
- if someone tries to click on the close icon, you get an error
what do you think? Maybe disposing of the class after hidden is not a good idea?
Soooo I found some time to try your code
Using your script with some minor changes (you will find below), seems, it works ok
const html = `<div id="liveToast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"> <div class="toast-header"> <strong class="me-auto">Bootstrap</strong> <small>11 mins ago</small> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button> </div> <div class="toast-body"> Hello, world! This is a toast message. </div> </div> </div>`; document.getElementById("toast-me").addEventListener("click", ev => { const container = document.getElementById("toast-container"); container.innerHTML = container.innerHTML + html; const toastElList = document.querySelectorAll(".toast"); console.log(toastElList.length); toastElList.forEach(toastEl => { const inst = bootstrap.Toast.getOrCreateInstance(toastEl, { autohide: true }); inst.show(); toastEl.addEventListener( "hidden.bs.toast", () => { // can cause issue with bs5 // @link https://github.com/twbs/bootstrap/issues/37265 inst.dispose(); //toastEl.remove(); console.log(inst, toastEl); }, { once: true } ); }); setTimeout(() => { document.querySelectorAll(".btn-close").forEach((btn) => { btn.dispatchEvent(new Event("click")); console.log("click"); }); }, 5000); });
@GeoSot i updated the pen with your code, it’s still happening. you do have to click a crazy person to get the issue. I suspect there is some kind of race condition happening in rare cases when you click on the close icon (which is what i can gather from the error reporting in my production app)
@GeoSot Got the issue again on another project so i decided to have a look again
good thing is: i know what is the precise, reproducible error and updated the codepen accordingly
it happens if you double click fast on the close icon and if the instance was disposed on hidden callback.
- removing animations fixes the issue => there is no delay in queue callback and therefore, double clicking has no impact
- delaying the dispose with a timeout works also => this is what i will be doing for now
- disabling pointer events on hide also seem to work just fine
therefore, i don’t think there is anything really wrong with bootstrap itself if you don’t dispose of toast instances on hidden. This is still a (minor) issue as soon as you try to create dynamic instances like I do and need to dispose of them afterwards and remove the actual html element from the dom.
in the same line of thinking, you can get similar issue with dynamic models where you get
undefined is not an object (evaluating ‘this._config.backdrop’)
some kind of issue: dispose&removing on hidden can lead to this
The «TypeError: Cannot read property ‘classList’ of null» error in JavaScript occurs when you try to access the property of an object that is null or undefined. This error can occur when trying to manipulate the class list of an element in the DOM, but the element is not present in the HTML or is not yet loaded by the time you are trying to access it. This error can cause your JavaScript code to fail and stop executing, so it’s important to understand how to resolve it. Here are a few methods to solve this issue:
Method 1: Use a Conditional Statement
To fix the TypeError: Cannot read property 'classList' of null
error in JavaScript, you can use a conditional statement to check if the element exists before trying to access its classList
property. Here’s an example code snippet that demonstrates how to do this:
let element = document.getElementById('my-element');
if (element !== null) {
element.classList.add('my-class');
}
In this example, we first try to get the element with the ID my-element
using the getElementById
method. If the element exists, we add the my-class
class to its classList
property using the add
method.
Another way to use a conditional statement to fix this error is to use the optional chaining operator (?.
), which allows you to access nested properties without throwing an error if one of the properties is null. Here’s an example code snippet that demonstrates how to use the optional chaining operator:
let element = document.getElementById('my-element');
element?.classList.add('my-class');
In this example, we use the optional chaining operator to access the classList
property of the element
variable. If element
is null, the expression will simply return undefined and no error will be thrown.
Overall, using a conditional statement to check if an element exists before trying to access its classList
property is a simple and effective way to fix the TypeError: Cannot read property 'classList' of null
error in JavaScript.
Method 2: Check for the Element’s Presence Before Accessing its Properties
If you are facing the error «TypeError: Cannot read property ‘classList’ of null» in JavaScript, it means that you are trying to access the classList
property of a null element. This error can occur when you are trying to manipulate the DOM and the element you are trying to access does not exist.
One way to fix this error is to check for the presence of the element before accessing its properties. Here’s how you can do it:
const element = document.getElementById('my-element');
if (element) {
element.classList.add('my-class');
}
In the code above, we first check if the element with the ID my-element
exists using document.getElementById()
. If the element exists, we add the my-class
class to its classList
.
You can also use the querySelector()
method to select an element and then check for its presence before accessing its properties:
const element = document.querySelector('.my-element');
if (element) {
element.classList.add('my-class');
}
In the code above, we select the element with the class my-element
using document.querySelector()
. If the element exists, we add the my-class
class to its classList
.
You can also use the querySelectorAll()
method to select multiple elements and then loop through them to check for their presence before accessing their properties:
const elements = document.querySelectorAll('.my-element');
elements.forEach(element => {
if (element) {
element.classList.add('my-class');
}
});
In the code above, we select all the elements with the class my-element
using document.querySelectorAll()
. We then loop through each element using forEach()
and check if it exists before adding the my-class
class to its classList
.
By checking for the presence of the element before accessing its properties, you can avoid the «TypeError: Cannot read property ‘classList’ of null» error in JavaScript.
Method 3: Wait for the DOM to Load Before Accessing the Element
To fix the TypeError: Cannot read property 'classList' of null
error in JavaScript, you can use the DOMContentLoaded
event to wait for the DOM to load before accessing the element. Here are the steps to do it:
- Wrap your JavaScript code that accesses the element inside the
DOMContentLoaded
event listener.
document.addEventListener('DOMContentLoaded', function() {
// Your code goes here
});
- Within the event listener, use the
getElementById
method to get the element you want to access.
document.addEventListener('DOMContentLoaded', function() {
const element = document.getElementById('my-element');
});
- Check if the element exists before accessing its
classList
property.
document.addEventListener('DOMContentLoaded', function() {
const element = document.getElementById('my-element');
if (element) {
element.classList.add('my-class');
}
});
Here’s the complete code example:
document.addEventListener('DOMContentLoaded', function() {
const element = document.getElementById('my-element');
if (element) {
element.classList.add('my-class');
}
});
This code waits for the DOM to load before accessing the element with the ID my-element
. It checks if the element exists before adding the class my-class
to its classList
. This prevents the TypeError: Cannot read property 'classList' of null
error from occurring.
Method 4: Use Event Delegation
To fix the TypeError: Cannot read property 'classList' of null
error in JavaScript, you can use event delegation. Event delegation is a technique where you add an event listener to a parent element instead of adding it to each individual child element. This way, you can handle events on multiple elements with a single event listener. Here’s how you can use event delegation to fix the error:
- First, select the parent element that contains the child element(s) that you want to add the event listener to. For example, if you want to add a click event listener to all buttons inside a div with an ID of «myDiv», you can select the div like this:
const myDiv = document.querySelector('#myDiv');
- Next, add an event listener to the parent element using the
addEventListener()
method. In this example, we’ll add a click event listener:
myDiv.addEventListener('click', (event) => {
// code to handle the event goes here
});
- Inside the event listener, use the
event.target
property to get the child element that was clicked. This property returns the element that triggered the event:
myDiv.addEventListener('click', (event) => {
const clickedElement = event.target;
// code to handle the event goes here
});
- Check if the clicked element is the one you want to add the class to. In this example, we’ll check if the clicked element is a button:
myDiv.addEventListener('click', (event) => {
const clickedElement = event.target;
if (clickedElement.tagName === 'BUTTON') {
// code to handle the event goes here
}
});
- Finally, add the class to the clicked element:
myDiv.addEventListener('click', (event) => {
const clickedElement = event.target;
if (clickedElement.tagName === 'BUTTON') {
clickedElement.classList.add('active');
}
});
Here’s the complete code:
const myDiv = document.querySelector('#myDiv');
myDiv.addEventListener('click', (event) => {
const clickedElement = event.target;
if (clickedElement.tagName === 'BUTTON') {
clickedElement.classList.add('active');
}
});
This code adds a click event listener to the div with an ID of «myDiv». When a button inside the div is clicked, the code checks if the clicked element is a button. If it is, the code adds the «active» class to the clicked button. This way, you can avoid the TypeError: Cannot read property 'classList' of null
error and handle events on multiple elements with a single event listener.
Issue
I am having this header which on scroll, I want to change the background to a different color.
The navbar
variable returns null always. Why is it so? How can I do that?
The transform:translateY
is just for a small animation on scroll.
import React from 'react'
import { Navbar, Container, Nav } from 'react-bootstrap'
import { LinkContainer } from 'react-router-bootstrap'
const Header = () => {
const navbar = document.getElementById("navbar");
let scrolled = false;
window.onscroll = function () {
if (document.body.scrollTop >= 200 || document.documentElement.scrollTop >= 200) {
navbar.classList.add('color-nav');
if (!scrolled) {
navbar.style.transform = 'translateY(-70px)'
}
setTimeout(function () {
navbar.style.transform = 'translateY(0px)'
scrolled = true
}, 200)
} else {
navbar.classList.remove('color-nav');
scrolled = false
}
};
return (
<div id='navbar' >
<Navbar fixed="top" className='navbar' collapseOnSelect expand="lg" >
<Container>
<LinkContainer to='/'>
<Navbar.Brand className='logo' >Logo</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto">
<LinkContainer to='/services'>
<Nav.Link className='links'>Services</Nav.Link>
</LinkContainer>
</Nav>
<Nav>
<LinkContainer to='/login'>
<Nav.Link className='links'>Login</Nav.Link>
</LinkContainer>
<LinkContainer to='/signup'>
<Nav.Link className='links'>
Sign Up
</Nav.Link>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div >
)
}
export default Header
You are going about things in what I’d like to call, «against the React way». To access the navbar element, you should be utilizing the useRef
hook. This will give you an easy reference to that element through the components lifecycle.
On top of that, there are some other issues that need to be taken care of:
- The
scrolled
variable not being used withuseState
- Your scroll listener not being set in a
useEffect
. Right now, your code would set a new listener on every re-render of your component. You want to just set it once. - Your scroll listener not being cleaned up.
I’ve made some changes to the code that I would hope will fix this issue for you.
import React, { useState, useRef, useEffect } from "react";
import { Navbar, Container, Nav } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap";
const Header = () => {
const [scrolled, setScrolled] = useState(false);
const navRef = useRef();
useEffect(() => {
const handleScroll = () => {
if (
document.body.scrollTop >= 200 ||
document.documentElement.scrollTop >= 200
) {
navRef.current.classList.add("color-nav");
if (!scrolled) {
navRef.current.style.transform = "translateY(-70px)";
}
setTimeout(function () {
navRef.current.style.transform = "translateY(0px)";
setScrolled(true);
}, 200);
} else {
navRef.current.classList.remove("color-nav");
setScrolled(false);
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div id="navbar" ref={navRef}>
<Navbar fixed="top" className="navbar" collapseOnSelect expand="lg">
<Container>
<LinkContainer to="/">
<Navbar.Brand className="logo">Logo</Navbar.Brand>
</LinkContainer>
<Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav">
<Nav className="me-auto">
<LinkContainer to="/services">
<Nav.Link className="links">Services</Nav.Link>
</LinkContainer>
</Nav>
<Nav>
<LinkContainer to="/login">
<Nav.Link className="links">Login</Nav.Link>
</LinkContainer>
<LinkContainer to="/signup">
<Nav.Link className="links">Sign Up</Nav.Link>
</LinkContainer>
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
</div>
);
};
export default Header;
Answered By — Dan Zuzevich