I am getting an error
TypeError: Cannot read property 'classList' of null
I am not really sure what am I doing wrong in manipulating DOM element for each form field. My form field has an stated name and ID of form and each input field has a name and id of the field as well. But I am still getting an error.
What am i doing wrong?
My script:
document.querySelector("#form").addEventListener("submit", function(e){
//create variable for contact form url
var formURL = 'melonForm.php';
//prevent default submission
event.preventDefault();
//define form fields
var melonForm = {
'firstName' : document.querySelector('input[name=firstName]').value,
'lastName' : document.querySelector('input[name=lastName]').value,
'companyName' : document.querySelector('input[name=companyName]').value,
'companyAddress' : document.querySelector('input[name=companyAddress]').value,
'city' : document.querySelector('input[name=city]').value,
'state' : document.querySelector('select[name=state]').value,
'zipcode' : document.querySelector('input[name=zipcode]').value,
'emailAddress' : document.querySelector('input[name=emailAddress]').value,
'phoneNumber' : document.querySelector('input[name=phoneNumber]').value,
}
//define request variable
var formRequest = new Request(formURL, {
method: 'POST',
body: melonForm,
headers: new Headers()
});
//fetch
fetch(formRequest)
.then(function(formResponse) {
return formResponse.json();
})
.then(function(data) {
//handle server responses
if ( ! data.success) {
//handle error messages
//handle error message for firstName
console.log(data);
if (data.errors.firstName) {
document.getElementById("firstName").classList.add("has-error");
document.getElementById("firstName").appendChild('<div class="help-block">' + data.errors.firstName + '</div>');
}
//handle errors for lastName
if (data.errors.lastName) {
document.getElementById("lastName").classList.add("has-error");
document.getElementById("lastName").appendChild('<div class="help-block">' + data.errors.lastName + '</div>');
}
//handle errors for companyName
if (data.errors.companyName) {
document.getElementById("companyName").classList.add("has-error");
document.getElementById("companyName").appendChild('<div class="help-block">' + data.errors.companyName + '</div>');
}
//handle errors for companyAddress
if (data.errors.companyAddress) {
document.getElementById("companyAddress").classList.add("has-error");
document.getElementById("companyAddress").appendChild('<div class="help-block">' + data.errors.companyAddress + '</div>');
}
//handle errors for city
if (data.errors.city) {
document.getElementById("city").classList.add("has-error");
document.getElementById("city").appendChild('<div class="help-block">' + data.errors.city + '</div>');
}
//handle errors for state
if (data.errors.state) {
document.getElementById("state").classList.add("has-error");
document.getElementById("statea").appendChild('<div class="help-block">' + data.errors.state + '</div>');
}
//handle errors for zipcode
if (data.errors.zipcode) {
document.getElementById("zipcode").classList.add("has-error");
document.getElementById("zipcode").appendChild('<div class="help-block">' + data.errors.zipcode + '</div>');
}
//handle errors for emailAddress
if (data.errors.emailAddress) {
document.getElementById("emailAddress").classList.add("has-error");
document.getElementById("emailAddress").appendChild('<div class="help-block">' + data.errors.emailAddress + '</div>');
}
//handle errors for phoneNumber
if (data.errors.phoneNumber) {
document.getElementById("phoneNumber").classList.add("has-error");
document.getElementById("phoneNumber").appendChild('<div class="help-block">' + data.errors.phoneNumber + '</div>');
}
// handle errors for captcha ---------------
if (data.errors.captcha) {
swal({
title: "Error!",
text: data.errors.captcha,
icon: "error",
});
}
// handle errors for phpmailer ---------------
if (data.message) {
swal({
title: "Error!",
text: data.message,
icon: "error",
});
}
else {
//handle success messages
swal({
title: "Success!",
text: data.message,
icon: "success",
});
document.getElementById("form").reset();
}
}
});
})
I know that the field firstName does exist in the html but not sure why the javascript is not able to read the element.
ADDED HTML:
<html>
<head>
<title>Melon Form</title>
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css"> <!-- load bootstrap via CDN -->
<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
</head>
<body>
<div class="col-sm-6 col-sm-offset-3">
<h1>Contact Form</h1>
<!-- OUR FORM -->
<form name="form" id="form" action="melonForm.php" method="POST">
<!-- FIRST NAME -->
<div id="firstName-group" class="form-group">
<label for="firstName">First Name:</label>
<input type="text" class="form-control" name="firstName" placeholder="Henry Pym">
<!-- errors will go here -->
</div>
<!-- LAST NAME -->
<div id="lastName-group" class="form-group">
<label for="lastName">Last Name:</label>
<input type="text" class="form-control" name="lastName" placeholder="Henry Pym">
<!-- errors will go here -->
</div>
<!-- COMPANY NAME -->
<div id="companyName-group" class="form-group">
<label for="companyName">Company Name:</label>
<input type="text" class="form-control" name="companyName" placeholder="Henry Pym">
<!-- errors will go here -->
</div>
<!-- COMPANY ADDRESS -->
<div id="companyAddress-group" class="form-group">
<label for="companyAddress">Company Address:</label>
<input type="text" class="form-control" name="companyAddress" placeholder="Henry Pym">
<!-- errors will go here -->
</div>
<!-- CITY -->
<div id="city-group" class="form-group">
<label for="city">City:</label>
<input type="text" class="form-control" name="city" id="city" placeholder="Henry Pym">
<!-- errors will go here -->
</div>
<div id="state-group" class="form-group">
<label for="state">State</label>
<select id="state" name="state" class="form-control">
<option value="" selected>Choose...</option>
<option value="AL">Alabama</option>
<option value="AK">Alaska</option>
<option value="AZ">Arizona</option>
<option value="AR">Arkansas</option>
<option value="CA">California</option>
<option value="CO">Colorado</option>
<option value="CT">Connecticut</option>
<option value="DE">Delaware</option>
<option value="DC">District Of Columbia</option>
<option value="FL">Florida</option>
<option value="GA">Georgia</option>
<option value="HI">Hawaii</option>
<option value="ID">Idaho</option>
<option value="IL">Illinois</option>
<option value="IN">Indiana</option>
<option value="IA">Iowa</option>
<option value="KS">Kansas</option>
<option value="KY">Kentucky</option>
<option value="LA">Louisiana</option>
<option value="ME">Maine</option>
<option value="MD">Maryland</option>
<option value="MA">Massachusetts</option>
<option value="MI">Michigan</option>
<option value="MN">Minnesota</option>
<option value="MS">Mississippi</option>
<option value="MO">Missouri</option>
<option value="MT">Montana</option>
<option value="NE">Nebraska</option>
<option value="NV">Nevada</option>
<option value="NH">New Hampshire</option>
<option value="NJ">New Jersey</option>
<option value="NM">New Mexico</option>
<option value="NY">New York</option>
<option value="NC">North Carolina</option>
<option value="ND">North Dakota</option>
<option value="OH">Ohio</option>
<option value="OK">Oklahoma</option>
<option value="OR">Oregon</option>
<option value="PA">Pennsylvania</option>
<option value="RI">Rhode Island</option>
<option value="SC">South Carolina</option>
<option value="SD">South Dakota</option>
<option value="TN">Tennessee</option>
<option value="TX">Texas</option>
<option value="UT">Utah</option>
<option value="VT">Vermont</option>
<option value="VA">Virginia</option>
<option value="WA">Washington</option>
<option value="WV">West Virginia</option>
<option value="WI">Wisconsin</option>
<option value="WY">Wyoming</option>
</select>
</div>
<!-- ZIPCODE -->
<div id="zipcode-group" class="form-group">
<label for="zipcode">Zipcode:</label>
<input type="text" class="form-control" name="zipcode" id="zipcode" placeholder="12345">
<!-- errors will go here -->
</div>
<!-- EMAIL ADDRESS -->
<div id="emailAddress-group" class="form-group">
<label for="emailAddress">Email Address:</label>
<input type="text" class="form-control" name="emailAddress" placeholder="rudd@avengers.com">
<!-- errors will go here -->
</div>
<!-- PHONE NUMBER -->
<div id="phoneNumber-group" class="form-group">
<label for="phoneNumber">Phone Number:</label>
<input type="text" class="form-control" name="phoneNumber" id="phoneNumber" placeholder="12345">
<!-- errors will go here -->
</div>
<!-- MESSAGE -->
<div id="message-group" class="form-group">
<label for="message">Message:</label>
<input type="text" class="form-control" name="message" placeholder="Ant Man">
<!-- errors will go here -->
</div>
<!-- GOOGLE RECAPTCHA -->
<div class="form-group">
<div class="g-recaptcha" data-sitekey="SECRET_KEY"></div>
</div>
<button type="submit" class="btn btn-success">Submit <span class="fa fa-arrow-right"></span></button>
</form>
</div>
<script src="melonForm.js" defer></script> <!-- load our javascript file -->
</body>
<script src='https://www.google.com/recaptcha/api.js'></script>
</html>
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