Why the button area is not working as I expected? - javascript

I am trying to make an auto scroll tool for the browser(Chrome). What i want is a button, so when i click the button, the page should auto scroll down. By the way, I am using an application, whenever i visit a website, the application will inject the script below in the website. Here is my script:
<script>
let btn = document.createElement("button");
var isScrolling = false;
btn.innerHTML = "start scroll";
btn.style.cssText = "width:50px;height:50px;float:right;background:grey;position:fixed;right:0;top:50%;opacity:0.5;border-radius:50%"
btn.addEventListener("click", () => onclick);
let autoScroll = () => {if(isScrolling){window.scrollBy(0, 1);setTimeout(autoScroll, 10)}};
onclick = function() {console.log("click"); isScrolling=!isScrolling; autoScroll()};
document.body.appendChild(btn);
</script>
The problem is when I click the other areas of the page(not the button area), it still will trigger the onclick function. What i want is the page only scroll down when i click the button area, not any other area of the page. Does anyone know where the problem is? You can copy and run the script in the chrome console, any help would appreciate!

The problem is that you are storing your onclick function into a global one, because you are not defining it before. This works correctly:
<script>
let btn = document.createElement("button");
var isScrolling = false;
const onclick = function() {console.log("click"); isScrolling=!isScrolling; autoScroll()};
btn.innerHTML = "start scroll";
btn.style.cssText = "width:50px;height:50px;float:right;background:grey;position:fixed;right:0;top:50%;opacity:0.5;border-radius:50%"
btn.addEventListener("click", onclick);
let autoScroll = () => {if(isScrolling){window.scrollBy(0, 1);setTimeout(autoScroll, 10)}};
document.body.appendChild(btn);
</script>
Pay attention that i'm defining the onclick before using it, and that it is directly used in addEventListener, you shouldn't return it in the arrow function:
// This is WRONG
btn.addEventListener("click", () => onclick);
// This is OK
btn.addEventListener("click", onclick);

I believe you are running into conflicts with your variables and the scripts already on the page. If injecting this script, you can break javascript code already on the page.
To get the variables into their own frame, try this:
<script>
(function () {
let btn = document.createElement("button");
var isScrolling = false;
btn.innerHTML = "start scroll";
btn.style.cssText =
"width:50px;height:50px;float:right;background:grey;position:fixed;right:0;top:50%;opacity:0.5;border-radius:50%"
let onclick = function() {console.log("click"); isScrolling=!isScrolling; autoScroll()};
btn.addEventListener("click", onclick);
let autoScroll = () => {if(isScrolling){window.scrollBy(0, 1);
setTimeout(autoScroll, 10)}};
document.body.appendChild(btn);
})();
</script>

The problem is in this line:
btn.addEventListener("click", () => onclick);
You would want either:
btn.addEventListener("click", onclick); // binding a reference for later execution
or
btn.addEventListener("click", () => onclick());
However, if you want to later btn.removeEventListener to prevent memory leaks, you would rather want the former variant
It would also be better code if you first declared the onclick handler and then bound it to button, not the other way around :)

Related

How to add a button to run code inside LeafletJS marker?

I am trying to add a button inside a pointer which print a log to the console. This is just meant to be a test, so I can actually make the marker run a method, but I can't even get it to print text.
const marker = L.marker([latitude, longitude]).addTo(map);
const button = '<br/><button type="button">Click button</button>'
const clickbutton = document.querySelector("#click");
button1.addEventListener("click", function(event) {
console.log('This button works!');
});
marker.bindPopup(button);
When I load the page, I immediately get this error:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'addEventListener')
The console says this error is caused by
button1.addEventListener("click", function(event) {
but I'm not sure why it's null. Can anyone help me out?
You are creating the button incorrectly.
It will be right this way:
const button = document.createElement('button');
button.id = 'delete';
button.textContent = 'Delete marker';
In order to add a button to the page, you need to find the desired parent element and add the button as a child element:
// replace this id with what you need
const buttonParrentId = 'button-parrent-id';
const buttonParrent = document.getElementById(buttonParrentId);
buttonParrent.appendChild(button);
Next, you can work with the button as you need:
const marker = L.marker([latitude, longitude]).addTo(map);
button.addEventListener("click", function(event) {
console.log('This button works!');
});
marker.bindPopup(button);
Result code example:
const button = document.createElement('button');
button.id = 'delete';
button.textContent = 'Delete marker';
// replace this id with what you need
const buttonParrentId = 'button-parrent-id';
const buttonParrent = document.getElementById(buttonParrentId);
buttonParrent.appendChild(button);
const marker = L.marker([latitude, longitude]).addTo(map);
button.addEventListener("click", function(event) {
console.log('This button works!');
});
marker.bindPopup(button);
More about createElement

Button Doesn't Work If Created Using innerHTML

I have this kind of structure
<button ng-click="something(1)">click!</button>
<div id="place"></div>
something() works in this situation, but if I try to make another button with innerHTML on a js code
str="<button ng-click=\"something(2)\">click 2!</button>"
document.getElementById("place").innerHTML = str;
thrn the 2nd button, the one created by innerHTML, apears normal, but doesn't call the function something() when clicked
Is there a nother way to add the button? the buttons should be generated proceduraly as the page runs
Thank you very much!
<script>
let btn = document.createElement("button");
btn.innerText = "click2"
// class
btn.classList = [];
// on click function
btn.onclick = () => {
}
document.getElementById("place").innerHTML = "";
document.getElementById("place").appendChild(btn)
</script>

For Each is not Working for Created Element By Click

What I want : Whenever i click on any "hello" world it should alert me Hello, even on these "Hello's" they are Created By Button
Here is My code I tried For(of) and ForEach() but nothing works for me
Here is my try
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.classList.add("hello");
document.body.appendChild(NewP);
}
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = function() {
window.alert("Hello")
}
})
<p class="hello">Hello</p>
<button id="kick">Create ME</button>
New in Javascript
forEach only gets called at the beginning of this code, before the user has added any elements, so the onclick event is only added to the first one. You need to add the onclick function to each newly added element:
btn.onclick = function(){
let NewP = document.createElement("p");
...
NewP.onclick = function(){
window.alert("Hello")
}
}
You can add the onclick event when you create the new element.
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.onclick = popup;
NewP.classList.add("hello");
document.body.appendChild(NewP);
}
function popup() {
window.alert("Hello")
}
<p class="hello" onclick="popup()">Hello</p>
<button id="kick">Create ME</button>
You have to distinguish between synchronous and asynchronous code.
let btn = document.getElementById("kick");
btn.onclick = async;
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = async;
})
So, at the moment forEach is called, the btn.onclick handler wasn't called, reaching only <p class="hello"> present on the DOM.
Taking your code, the moment to assing the .hello handler is when you create the element...
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
NewP.classList.add("hello"); // you dont need this if isn't used by CSS
NewP.onclick = handleHelloClick;
document.body.appendChild(NewP);
}
let allClasshello = document.getElementsByClassName("hello");
let allClasshelloAr = Array.from(allClasshello);
allClasshelloAr.forEach(function popup(elemetnWithHello) {
elemetnWithHello.onclick = handleHelloClick;
})
function handleHelloClick() {
window.alert("Hello")
}
If you had a container for those <p> you can take advantage to the addEventListener API, subscribing to the container instead of each element...
let container = document.createElement("div");
container.addEventListener("click", containerClickHandler);
document.body.appendChild(container);
function containerClickHandler() {
window.alert("Hello");
}
let btn = document.getElementById("kick");
btn.onclick = function() {
let NewP = document.createElement("p");
let createText = document.createTextNode("Hello");
NewP.appendChild(createText);
container.appendChild(NewP);
}
When the forEach is run there is no paragraph element (NewP). To solve this problem there are two options I suggest:
Add the onclick event listener to NewP element where you are creating it.
Have another action that calls your below code (wrapped inside a function) on-demand to assign onclick event listener
Here are the sample working solutions:
Option 1 Solution - Add event listener upfront
Option 2 Solution - Add event listener on demand
You need an event handler to be set up so that you know when an element is clicked. My example uses a technique called event delegation which basically means we add a single event handler to a parent DOM element - then, when the event bubbles up, we check the event.target to see the exact element that was clicked.
Something like below - check the comments in the code:
// Vars to keep a reference to your DOM elements
const buttonEl = document.querySelector('#buttonEl');
const containerEl = document.querySelector('#containerEl');
// Event handling functions
const pHandler = e => {
if (e.target.tagName === 'P') { //This line also part of the event delegation
console.log(e.target.textContent);
}
};
const buttonHandler = (e, containerEl) => {
const pEl = document.createElement("p");
pEl.appendChild(document.createTextNode("Hello"));
containerEl.appendChild(pEl);
};
//Add the click handlder to the button so it will create more elements when clicked
buttonEl.addEventListener('click', e => buttonHandler(e, containerEl))
//Add a click handler to the parent container - this is called event delegation
containerEl.addEventListener('click', pHandler)
<input id="buttonEl" type="button" value="Create HELLO" />
<div id="containerEl">
<p class="hello">Hello</p>
</div>
For dynamically created elements you'd have to attach the vent listener (in this case it's a click event) when you're creating them (and before appending them to the document).
See the next example :
const btn = document.getElementById('kick'),
alreadyInDocHello = document.querySelectorAll('.hello'); /** selecting the already in the document element to handle click event (those created dynamically will not be included here) **/
sayHello = e => {
e && e.preventDefault();
alert('Hello !');
}; /** sayHello is the function that will be called when having a click event (basically it'll show an alert saying "hello !" **/
/** add click listener for create button **/
btn.addEventListener('click', e => {
let newEl = document.createElement('p'),
createText = document.createTextNode('Hello');
e.preventDefault();
newEl.appendChild(createText);
newEl.classList.add('hello');
/** add the listener here **/
newEl.addEventListener('click', sayHello); /** don't forget that sayHello is declared above **/
document.body.appendChild(newEl);
});
/** click event listener for those elements that are already present in the document **/
alreadyInDocHello.forEach(el => el.addEventListener('click', sayHello));
.hello {
background: #242424;
color: #fff;
padding: 15px;
text-align: center;
transition: all .4s 0s ease;
}
.hello:hover {
background: #ccc;
color: #000;
}
<p class="hello">Hello</p>
<button id="kick">Create ME</button>
Learn more about addEventListener function.

Why can't I assign a function to Object.prototype.click?

I know how to create dynamic elements and append them to existing ones, and how to create click events and attach them. See my snippet below
var container = document.getElementById("container");
var btn1 = document.createElement("button");
var btn2 = document.createElement("button");
btn1.innerHTML = "button 1";
btn2.innerHTML = "button 2";
btn1.onclick = function(){ console.log('button 1 clicked'); }; // this works fine
btn2.onclick = function(){ console.log('button 2 clicked'); };
container.append(btn1);
container.append(btn2);
<div id="container"></div>
I am trying to mimic jQuery's .click() behavior. (I don't want to use jQuery, I just need a shorter way of assigning click events to dynamically created elements).
What I mean, is I would like to extend Object.prototype by adding a function that accepts a function as the parameter, and all it does behind the scenes is assign it to the object's .onclick property.
However, the code runs, but no click event is triggered, and no error is reported.
var container = document.getElementById("container");
var btn1 = document.createElement("button");
var btn2 = document.createElement("button");
btn1.click(function(){ console.log('button 1 clicked'); }); // this doesn't work
btn2.click(function(){ console.log('button 2 clicked'); }); // this doesn't work
btn1.innerHTML = "button 1";
btn2.innerHTML = "button 2";
container.append(btn1);
container.append(btn2);
Object.prototype.click = function(fn) {
console.log('this function never gets executed');
this.onclick = fn;
}
<div id="container"></div>
I can tell that my extension of Object.prototype.click has not been executed since the console.log() never happens.
Is it because Object.prototype.click is reserved? If so, why am I not being able to override its definition?
What am I doing wrong?
There are two issues. First, you should assign to the prototype before you try calling btn1.click(), else the native .click() will be called. (The native .click() method of an element programatically triggers a click event on that element.)
The other problem is that btn1 is an element, not just an object, and HTMLElements have a click property on their prototype. So, even if you assign to Object.prototype.click, HTMLElement.prototype.click will be seen sooner in the prototype chain, and so your Object.prototype.click will be ignored. You'll have to either overwrite HTMLElement.prototype.click or HTMLButtonElement.click:
var container = document.getElementById("container");
var btn1 = document.createElement("button");
var btn2 = document.createElement("button");
HTMLElement.prototype.click = function(fn) {
console.log('setting up click event');
this.onclick = fn;
}
btn1.click(function(){ console.log('button 1 clicked'); });
btn2.click(function(){ console.log('button 2 clicked'); });
btn1.innerHTML = "button 1";
btn2.innerHTML = "button 2";
container.append(btn1);
container.append(btn2);
<div id="container"></div>
But mutating the built-in objects like this isn't very good practice and can lead to things breaking (such as if a library assumes that .click() will trigger the normal HTMLElement.prototype.click function) - you might consider assigning click property directly to the instance, rather than to the prototype:
var container = document.getElementById("container");
function myCreateElement(type) {
const elm = document.createElement(type);
elm.click = function(fn) {
console.log('setting up click event');
this.onclick = fn;
}
return elm;
}
var btn1 = myCreateElement("button");
var btn2 = myCreateElement("button");
btn1.click(function(){ console.log('button 1 clicked'); });
btn2.click(function(){ console.log('button 2 clicked'); });
btn1.innerHTML = "button 1";
btn2.innerHTML = "button 2";
container.append(btn1);
container.append(btn2);
<div id="container"></div>

Toggle Event Listeners

I am trying to make a function that would allow me to toggle eventListener of an element.
In the example below, I have three buttons: main, on and off. When I click on the on button, the main button becomes functional. After I click off button, the main button should not work anymore (but now it still does).
Now I can achieve a desired behavior by clicking on button for the second time, but I guess it's a bad coincidence and it's not supposed to work that way.
Maybe I should add that I would like to work this out without using jQuery or similar and it needs to be a function, because I am going to use it for a lot of buttons.
(I suspect something with scope causes the problem (clickHandler when calling the function to activate the button is not the same as the clickHandler when calling the function to disable the button), but I can't think of a way to test it.)
// buttons definitions, not important
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButton = document.querySelector("#offButton");
// main function
var toggleButtons = function(toggleVal, button, element) {
var activateButton, clickHandler, disableButton;
// callback function for listener bellow
clickHandler = function() {
document.querySelector(element).classList.toggle("yellow");
};
activateButton = function() {
button.addEventListener("click", clickHandler);
};
disableButton = function() {
button.removeEventListener("click", clickHandler);
};
// when first argument is 1, make the button functional, otherwise disable its functionality
if (toggleVal === 1) {
activateButton();
} else {
disableButton();
}
};
// when onButton is clicked, call main function with arguments
// this works
onButton.addEventListener("click", function() {
toggleButtons(1, mainButton, "body");
});
// this fails to disable the button
offButton.addEventListener("click", function() {
toggleButtons(0, mainButton);
});
.yellow {
background-color: yellow;
}
<button type="button" id="mainButton">mainButton
</button>
<button type="button" id="onButton">onButton
</button>
<button type="button" id="offButton">offButton
</button>
<p>mainButton: toggles background color on click
</p>
<p>onButton: turns on mainButtons's functionality</p>
<p>offButton: supposed to turn off mainButton's functionality</p>
var mainButton = document.querySelector("#mainButton");
var onButton = document.querySelector("#onButton");
var offButon = document.querySelector("#offButton");
var element; // declare the element here and change it from toggleButtons when needed.
function clickHandler() {
document.querySelector(element).classList.toggle('yellow');
}
function activateButton(button) { // You missed this part
button.addEventListener('click', clickHandler);
}
function disableButton(button) { // You missed this part
button.removeEventListener('click', clickHandler);
}
function toggleButtons(value, button) {
if (value === 1) {
activateButton(button); // You missed this part
} else {
disableButton(button); // You missed this part
}
};
onButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(1, mainButton);
});
offButton.addEventListener('click', function() {
element = 'body'; // you can change it to some other element
toggleButtons(0, mainButton);
});
Below code helps to toggle between two functions from an eventListener:
var playmusic=false;
function playSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.currentTime = 0
audio.play()
playmusic=true;
}
function stopSound() {
const audio = document.querySelector(`audio[data-key="${event.keyCode}"]`)
audio.pause()
playmusic=false;
}
window.addEventListener('keydown',
function(){playmusic?stopSound():playSound()} )

Categories

Resources