javascript console log shows couple values - javascript

I am learning Javascript. I am creating simple budget application and I have a problem. I will pass only necessary code, to help me. So constructor is bigger, but I pass only necessary things:
class BudgetApp {
constructor() {
this.expenseListForm = document.querySelector("#expense-list");
}
deleteItemFromExpensesList(event) {
Array.from(event.currentTarget.querySelectorAll(".delete-icon")).forEach((element) => {
element.addEventListener("click", (e) => {
console.log(e.currentTarget);
})
})
}
}
eventListeners = () => {
const budgetApp = new BudgetApp();
budgetApp.expenseListForm.addEventListener("click", (event) => {
event.preventDefault();
budgetApp.deleteItemFromExpensesList(event);
})
}
document.addEventListener('DOMContentLoaded', () => {
eventListeners();
});
When I click ".delete-icon" which is <a> tag my console.log show nothing, when I click second time then console.log shows exactly what I want, when I click third time console_log shows this <a> tag two times and so on. I want to get result when I click one time, console_log shows me my <a> tag exactly once.
What should I improve in my code? Thanks Guys.

Related

JS DOM issue: Uncaught TypeError: Cannot read property 'addEventListener' of null becomes Uncaught Reference Error

This is code that i've downloaded from a tutorial here: (https://codepen.io/Web_Cifar/pen/PoNNEYY) in hopes of adapting it to something that i am working on. I've pieced it together and while it works in the demo (and there's a nice YT video where he goes thru it), it doesn't work for me in a live situation. I am attempting to build a Gravity Forms-like multi-page data entry form (GF has some quirks that don't work for me).
The first Javascript error that i got was: "Uncaught TypeError: Cannot read property 'addEventListener' of null". Researching that here on StackO, i got the idea that the general advice is that perhaps the DOM is not loaded for my document yet and we are calling JS before that has taken place, and two suggestions seem popular:
1). Move your JS file to the bottom of your HTML document, just before the close of your body tag (it WAS originally in my tag... moving it changed nothing).
2. Wrap your JS function in question in the following code: "window.addEventListener('DOMContentLoaded', (event) => {"
to force the DOM to load before calling the function.
Doing #2 changed the error to: ReferenceError: "changeStep" is not defined. There IS a function called 'changeStep' in the JS code. If you reference the tutorial above, you'll see that it was originally the last function defined in the JS file, so i thought that moving it to the top would change it. No dice. I've done some checks to see if some simple JQuery tests work in my environment and they do. I am relatively new to JS but i don't see why the DOM would not be loaded nor do i see why the function in question cannot be referenced.
Here is a skeleton of my HTML code:
<html>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script> /* A recommended test to see if jQuery is working, and it is. */
$(document).ready(function(){
$("button").click(function(){
alert("jQuery is working perfectly.");
});
});
</script>
<link rel="stylesheet" href="/Users/Me/Documents/multistep.css">
<body>
etc.....(moving to end of html file....)
<script type = "text/javascript" src = "/Users/Me/Documents/multistep.js" ></script>
</body>
</html>
And here is my entire JS script:
const steps = Array.from(document.querySelectorAll("form .step"));
const nextBtn = document.querySelectorAll("form .next-btn");
const prevBtn = document.querySelectorAll("form .previous-btn");
const form = document.querySelector("form");
window.addEventListener('DOMContentLoaded', (event) => {
function changeStep(btn) {
let index = 0;
const active = document.querySelector(".active");
index = steps.indexOf(active);
steps[index].classList.remove("active");
if (btn === "next") {
index++;
} else if (btn === "prev") {
index--;
}
steps[index].classList.add("active");
}
});
window.addEventListener('DOMContentLoaded', (event) => {
nextBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("next");
});
});
});
window.addEventListener('DOMContentLoaded', (event) => {
prevBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("prev");
});
});
});
window.addEventListener('DOMContentLoaded', (event) => {
form.addEventListener("submit", (e) => {
e.preventDefault();
const inputs = [];
form.querySelectorAll("input").forEach((input) => {
const { name, value } = input;
inputs.push({ name, value });
});
console.log(inputs);
form.reset();
});
});
Please note that i wrapped basically everything in the "window.addEventListener..." tag (as per advice Googled above) and the only other change i made from the referenced demo (that works in codePen) was to move the changeStep function to the first function in the file.
I'm at a loss as to what is going on. Can an experienced JS guy help me get un-stuck here?
changeStep is defined inside the anonymous callback function of the first event listener, so it is only visible inside that function. If you want to access it outside that function's scope, you have to define it outside of the function, i.e. before calling addEventListener. But a better solution is to put everything in one event listener. I do not see a reason why there have to be three of them.
Besides, since you are still calling querySelector outside the event listener, it does not solve the original problem. That is exactly the thing you want to do after the DOM context has been loaded, so you have to put it inside the event listener, too.
So the solution is to put basically everything in one event listener:
window.addEventListener('DOMContentLoaded', (event) => {
const steps = Array.from(document.querySelectorAll("form .step"));
const nextBtn = document.querySelectorAll("form .next-btn");
const prevBtn = document.querySelectorAll("form .previous-btn");
const form = document.querySelector("form");
function changeStep(btn) {
let index = 0;
const active = document.querySelector(".active");
index = steps.indexOf(active);
steps[index].classList.remove("active");
if (btn === "next") {
index++;
} else if (btn === "prev") {
index--;
}
steps[index].classList.add("active");
}
nextBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("next");
});
});
prevBtn.forEach((button) => {
button.addEventListener("click", () => {
changeStep("prev");
});
});
});
This should answer your question about why you get the second error, but actually putting the script at the end of the body tag should have solved the problem in the first place, so the first error probably has a different reason. Are you sure that your script tag was at the very end of the body tag, and that you actually have all the elements you are querying in your DOM tree?

Click event on a button with SetInterval Method

I'm not able to figure out how this can be accomplished. I'm working on the chrome extension and I have a setInterval method which monitors a button on a page and when its clicked, it runs a function. but the problem is, when I click the button, the function runs multiple times and I clearly understand this is because of the Interval function, but I want the setInterval to run always on this page so that I can monitor if the button is clicked or not. Below is my code
$(function(){
var url=window.location.href;
findAction(url);
}
function findAction(url){
setInterval(()=>{
Acceptbtn = $("[slot=primary-content-area")[4].querySelector("sn-inbox");
if(Acceptbtn !== undefined && Acceptbtn !== null){
Acceptbtn.addEventListener('click', myFunction);
}
function myFunction() {
console.log("clicked");
runAction(url)
};
},1000);
}
Is there any way to tackle this situation or have I made something simple, complicated or Is my approach completely wrong.?
Below is the Div which my extension monitors for the Accept button -
And once the Accept button is clicked, this is what happens
The Interval method keeps checking for this button and once found and I click on it, I want the runAction(url) function to execute. With my current code, the function gets executed but multiple times
Only add an event listener once
Also the selector is incorrect if sn-inbox is a class and [slot=primary-content-area] was missing a ]
Here I delegate - if you delegate to the closest static container (here document, because I do not know the closest container in your case), you can ALWAYS find the button when it is there:
const url = window.location.href;
$(document).on("click", "[slot=primary-content-area]", function() {
if ($(this).index() === 3) { // 0 based - so the 4th slot
const $inbox = $(this).find(".sn-inbox");
if ($inbox.length > 0) {
$inbox.html("running");
runAction(url)
}
}
})
Example code to execute the functions:
https://jsfiddle.net/mplungjan/p93cj0Le/
$(function() {
const runAction = url => console.log("running", url);
const url = window.location.href;
$(document).on("click", "[slot=primary-content-area]", function() {
if ($(this).index() === 3) { // 0 based
const $inbox = $(this).find(".sn-inbox");
if ($inbox.length > 0) {
$inbox.html("running");
runAction(url)
}
}
})
// testing the code
setInterval(() => {
$("#container").html(`<div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"></div>
<div slot="primary-content-area"><button class="sn-inbox">Click</button></div>
</div>`)
}, 5000)
setTimeout(() => $("#container").html(""), 2000)
})
<div id="container">
</div>

HTML Component That Is Fetched With JavaScript Not Responding To Click Event

I have an HTML component that is fetched when user clicks on a button. This component/modal is used to change a user's profile image (this is processed with PHP). The JavaScript fetch() happens with the following code:
var newProfileImageButton = document.getElementById('replace-profile-photo'),
changeProfileImageWrapper = document.getElementById('change-profile-image-wrapper'),
profileImageModalPath = "modals/change-profile-image.php";
newProfileImageButton.addEventListener('click', function(){
fetch(profileImageModalPath)
.then((response) => {
return response.text();
})
.then((component)=>{
changeProfileImageWrapper.innerHTML = component;
})
.catch((error => {console.log(error)}))
})
This fetched component includes a 'close' button should the user wish to close the modal and not update their profile image.
When I click the close button though nothing is happening. I have the script below - is the issue to do with the fact the Javascript has loaded before the component/modal is fetched? And if so how do I fix this? I guess I could just toggle display:none off and on for this component but would prefer to fetch it if possible.
Note: The button is responding to CSS hover events so I'm confident it's not an HTML / CSS markup issue.
// close button is part of the HTML component that is fetched
var closeComponent = document.getElementById('form-close-x')
if (closeComponent) {
closeComponent.addEventListener('click', function(){
// Hide the main component wrapper so component disappears
changeProfileImageWrapper.style.display = 'none';
})
}
I've also tried using the following code, which I found in a similar question, but this doesn't work either (it was suggested this was a duplicate question).
var closeComponent = document.getElementById('form-close-x')
if (closeComponent) {
closeComponent.addEventListener('click', function(e){
if (e.target.id == 'form-close-x') {
changeProfileImageWrapper.style.display = 'none';
}
})
}
Try this:
// var closeComponent = document.getElementById('form-close-x') <-- remove
// if (closeComponent) { <-- remove
document.addEventListener('click', function(e){
if (e.target.id === 'form-close-x') {
changeProfileImageWrapper.style.display = 'none';
}
})
//} <-- remove

addEventListener' of null

I don't know why it stills showing me addEventListerner of null and as you can see below I add window.onload function. it load the page and then it will break again showing me that the eventListener is null. But if I click on the X button in the page the error disappear and my page is return normal, except that the toggle Icon is not clickable again... Whicm means the event is not listening... please spare me some of your time and help me resolve this issue. Thank you
/*==================== SHOW NAVBAR ====================*/
const showMenu = (headerToggle, navbarId) =>{
const toggleBtn = document.getElementById(headerToggle),
nav = document.getElementById(navbarId)
// Validate that variables exist
window.onload=function(){
if(headerToggle && navbarId){
toggleBtn.addEventListener('click', ()=>{
// We add the show-menu class to the div tag with the nav__menu class
nav.classList.toggle('show-menu')
// change icon
toggleBtn.classList.toggle('bx-x')
})
}
}
}
showMenu('header-toggle','navbar')
/*==================== LINK ACTIVE ====================*/
const linkColor = document.querySelectorAll('.nav__link')
function colorLink(){
linkColor.forEach(l => l.classList.remove('active'))
this.classList.add('active')
}
linkColor.forEach(l => l.addEventListener('click', colorLink))
I hope I understand your problem correctly... if you are getting Cannot read property 'addEventListener' of null error that means that variable on which you are calling this function is null, and I suppose it's coming from this line toggleBtn.addEventListener('click', ()=>{. Make sure toggleBtn is not null, you can check that by using console.log(toggleBtn); before adding listener and then looking into console if it printed null or not. It is possible const toggleBtn = document.getElementById(headerToggle) can't find element with requested id. You haven't provided your HTML code, so that's only a guess.
Your code is not well structured, window.onload should be wrapping your code rather than the other way round. Also if you add an event listener on click, you'll be adding multiple event listeners and your function will fire multiple times. Try something like this (not tested)
const showMenu = (headerToggle, navbarId) => {
nav.classList.toggle("show-menu");
// change icon
toggleBtn.classList.toggle("bx-x");
};
window.onload = function () {
const toggleBtn = document.getElementById(headerToggle),
nav = document.getElementById(navbarId);
if (headerToggle && navbarId) {
toggleBtn.addEventListener("click", () => showMenu);
}
showMenu("header-toggle", "navbar");
};
/*==================== LINK ACTIVE ====================*/
const linkColor = document.querySelectorAll(".nav__link");
function colorLink() {
linkColor.forEach((l) => l.classList.remove("active"));
this.classList.add("active");
}
linkColor.forEach((l) => l.addEventListener("click", colorLink));

CKEditor 5 Insert text on button click - Pure JS

I'm trying to create a sort of dropdown menu that a user can insert tokens into a CKEditor with. I've found that I can insert text in an editor with this code:
editorInstance.model.change( writer => {
let pos = editorInstance.model.document.selection.getFirstPosition();
writer.insertText( "TEST", pos,0);
});
It works when I put it in the ClassicEditor.create.then for testing but it does nothing when I place this in a $().click event. I can log something from inside the callback so I know the code is executed but nothing is inserted.
I've been trying to get this working for hours now but all the examples I can find is in angular or any other frameworks.
I Solved the issue you faced as follows
first defined new editor that is null and Then instatiate Ckeditor 5
let neditor = null;
ClassicEditor.create(document.querySelector('#editor'))
.then(editor => {
neditor = editor;
})
.catch(error => {
console.error(error);
});
using pure js
document.addEventListener("click", (event) => {
var button = event.target;
if (event.target.type === 'button' && event.target.className.includes('testtokenbutton')) {
neditor.model.change(writer => {
const insertPosition = neditor.model.document.selection.getFirstPosition();
writer.insertText(button.id, insertPosition);
});
}
});

Categories