I hope someone can help me out with this one
My question is why does this code do exactly what I need?
var wfComponent;
fetch("https://nube-components.netlify.app/navbar01.json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => console.log(wfComponent));
document.addEventListener("copy", function (e) {
e.clipboardData.setData("application/json", wfComponent);
e.preventDefault();
});
document.getElementById("navbar01").onclick = function () {
document.execCommand("copy");
};
And this one does not do the copy to clipboard part?
$(".button.copy-button").on("click", function () {
let tag = $(this).attr("id");
console.log(tag);
var wfComponent;
fetch("https://nube-components.netlify.app/" + tag + ".json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => console.log(wfComponent));
document.addEventListener("copy", function (e) {
e.clipboardData.setData("application/json", wfComponent);
e.preventDefault();
});
document.getElementById(tag).onclick = function () {
document.execCommand("copy");
};
});
Now as you can see what I need is to "automate" that JSON location and target button part where I need each button to target a different URL. So I am now lost in this area where I manage to pull that id and apply it to the URL but the content does not get copied to the clipboard.
I am not a JS expert at all so please feel free to pinpoint anything I might be doing wrong or any ways to do this completely differently
Thanks
Because you use addEventListener inside the other. But either way, there is another (possibly hacky) way to achieve this.
$(".copy-button").on("click", function(e) {
let tag = $(this).attr("id");
fetch("https://nube-components.netlify.app/" + tag + ".json")
.then((res) => res.text())
.then((data) => (wfComponent = data))
.then(() => {
let copyFrom = document.createElement("textarea");
document.body.appendChild(copyFrom);
copyFrom.textContent = wfComponent;
copyFrom.select();
document.execCommand("copy");
copyFrom.remove();
console.log('COPIED!');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="navbar01" class="copy-button">copy navbar01</button>
<button id="navbar02" class="copy-button">copy navbar02</button>
I would like to run a script that has event listeners on a feed of posts. When I add a new post to the feed I have a function that makes a fetch call to add the post to the server and then proceed to update the DOM asynchronously. While I'm updating the DOM I would like to refresh the feed div where all the post are. I'm using an update() function that makes an XMLHttpRequest and updates the div, but after the new post is updated to the DOM I lose the initial script that contains event listeners for the posts.
function post() {
content = document.querySelector('#exampleFormControlTextarea1').value
console.log(content)
fetch('/add', {
method: 'POST',
body: JSON.stringify({
content: content,
})
})
.then(response => response.json())
.then(result => {
// Print result
if (result.error) {
document.querySelector('#message').className = "alert alert-danger rounded width_1"
document.querySelector('#message').innerText = result.error
document.querySelector("#message").style.display = 'block';
} else {
document.querySelector("#message").style.display = 'block';
document.querySelector('#message').className = "alert alert-success rounded width_1"
document.querySelector('#message').innerText = result.message
setTimeout(function () {
document.querySelector('#message').style.display = "none"
}, 2500)
setTimeout(function () {
document.querySelector("#add").style.display = 'none';
}, 500)
update()
console.log(result);
}
});
return false;
}
function update() {
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (this.status >= 200 && this.status < 400 && request.readyState == 4) {
// Success!
var resp = this.response;
let div = document.createElement('div')
div.innerHTML = resp
let content = div.querySelector('#jqpost').innerHTML
let post = document.createElement('div')
post.innerHTML = content
let feed = document.querySelector('#feed')
feed.insertAdjacentElement('beforebegin', post)
//console.log(script_text)
} else {
// We reached our target server, but it returned an error
// console.error(request.statusText);
}
};
request.open('GET', '/', true);
request.send();
}
How can I keep the event listeners script after I reload the feed?
No matter what I try, the .onclick or addEventListener 'click' will not work on my dynamically created buttons and I can't figure out why. As I was looking for solutions, I came across Event Delegation and I looked through 3 different websites and looked at the examples. I was sure this was going to solve my problem and I tried to mimic the examples but still it isn't working. I posted a question on here earlier but it was immediately removed because apparently it was too similar to another question (that was 12 years old!) but when I looked at that question they were using jQuery. I'm still a beginner in JS so I would prefer to understand how to resolve this in plain JS and I'm hoping this won't be removed.
This is my code:
document.addEventListener('DOMContentLoaded', function() {
userData();
document.querySelector('.list-group').addEventListener('click', function(e) {
if(e.target && e.target.nodeName == "BUTTON"){
console.log(e.target.id);
}
});
})
function userData() {
fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => {
const h6 = document.createElement("h6");
h6.innerText = "List of Users";
const userList = document.createElement("div");
userList.className = "list-group";
users.forEach(function(user) {
const userButton = document.createElement("button");
userButton.className = "list-group-item list-group-item-action";
userButton.id = `${user.id}`;
userButton.innerHTML = `
<strong>${user.name}</strong><br>
${user.email}<br>
${user.address.city}<br>
`;
userList.appendChild(userButton);
});
const container = document.querySelector('#response');
container.appendChild(h6);
container.insertBefore(userList, h6.nextSibling);
});
}
function userSelect(user_id) {
fetch(`https://jsonplaceholder.typicode.com/users/${user_id}`)
.then(response => response.json())
.then(user => {
console.log(user);
});
}
What I have now is a list of users and ultimately I want to be able to click on a user and bring up the full details of that user. At first I was trying to use the onclick function to redirect to the userSelect function but when that failed I looked around and found Event Delegation and still no luck. I tried to move the document.querySelector('.list-group) section down at the end of the userData function and still no luck. When I click on a button nothing shows up in console, if I use the userSelect function directly in console a user object appears. I'm at a real loss on how to get this to work. Please help!
Since function userData is making asynchronous call, the issue seems to be that you are adding the click event handler before the element with class '.list-group' got created.
You should use something like this to add click handler
document.addEventListener('DOMContentLoaded', function () {
userData().then(response => {
document.querySelector('.list-group').addEventListener('click', function (e) {
if (e.target && e.target.nodeName == "BUTTON") {
console.log(e.target.id);
}
})
});
})
Try below snippet:
document.addEventListener('DOMContentLoaded', function() {
userData().then(response => {
document.querySelector('.list-group').addEventListener('click', function(e) {
if (e.target && e.target.nodeName == "BUTTON") {
console.log(e.target.id);
}
})
});
})
function userData() {
return fetch("https://jsonplaceholder.typicode.com/users")
.then(response => response.json())
.then(users => {
const h6 = document.createElement("h6");
h6.innerText = "List of Users";
const userList = document.createElement("div");
userList.className = "list-group";
users.forEach(function(user) {
const userButton = document.createElement("button");
userButton.className = "list-group-item list-group-item-action";
userButton.id = `${user.id}`;
userButton.innerHTML = `
<strong>${user.name}</strong><br>
${user.email}<br>
${user.address.city}<br>
`;
userList.appendChild(userButton);
});
const container = document.querySelector('#response');
container.appendChild(h6);
container.insertBefore(userList, h6.nextSibling);
});
}
<div id="response">
</div>
or you can move the addEventListener code to end of userData
This is just another hopeless try to handle errors in async event handlers.
A note about this example: The example here works differently than it does if it is run directly in the browser. If ran directly in the browser none of the event listeners for errors is working ("error", "unhandledrejection").
It looks similar on Windows 10 in Chrome (Version 80.0.3987.163 (Official Build) (64-bit)) and Firefox (75.0 (64-bit)).
The only way I have found to handle this is to never make any typos. But that does not work either for me.
How is this supposed to work?
window.addEventListener("error", evt => {
console.warn("error event handler", evt);
output("error handler: " + evt.message, "yellow");
});
window.addEventListener("unhandledrejection", evt => {
console.warn("rejection event handler", evt);
output("rejection handler: " + evt.message, "green");
});
function output(txt, color) {
const div = document.createElement("p");
div.textContent = txt;
if (color) div.style.backgroundColor = color;
document.body.appendChild(div);
}
const btn = document.createElement("button");
btn.innerHTML = "The button";
btn.addEventListener("click", async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
})
document.body.appendChild(btn);
const btn2 = document.createElement("button");
btn2.innerHTML = "With try/catch";
btn2.addEventListener("click", async evt => {
evt.stopPropagation();
try {
output("Button 2 was clicked");
noFunction2(); // FIXME:
} catch (err) {
console.warn("catch", err)
throw Error(err);
}
})
document.body.appendChild(btn2);
new Promise(function(resolve, reject) {
setTimeout(function() {
return reject('oh noes');
}, 100);
});
justAnError();
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1">
<script defer src="error-test.js"></script>
EDIT - adding output from Chrome and JS Bin
(Link to JS Bin example)
Loading page
Chrome/Firefox:
error handler: Script error.
JS Bin:
error handler: Uncaught ReferenceError: justAnError is not defined
rejection handler: undefined
Clicking left button
Chrome/Firefox:
The button was clicked
JS Bin:
The button was clicked
rejection handler: undefined
You could give yourself utility functions for error reporting and wrapping event handlers, like this:
function handleError(err) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, "yellow");
}
function wrapHandler(fn) {
return function(evt) {
new Promise(resolve => {
resolve(fn(evt));
}).catch(e => {
handleError(e);
});
};
}
That supports both async and non-async event handlers. If there's a synchronous error calling fn, it's caught by the promise constructor and turned into a rejection of the promise being created. If there isn't, the promise is resolved to the return value of the fn, meaning that if fn returns a promise that rejects, the promise created by new Promise is rejected. So either way, errors go to the error handler.
I haven't tried to distinguish between errors and rejections, as they're fundamentally the same thing, but you could if you want:
function handleError(err, isRejection) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, isRejection ? "green" : "yellow");
}
function wrapHandler(fn) {
return function(evt) {
try {
const result = fn(event);
Promise.resolve(result).catch(e => handleError(e, true));
} catch (e) {
handleError(e, false);
}
};
}
Either way, you'd set up your global handlers to use it and prevent the default:
window.addEventListener("error", errorEvent => {
handleError(errorEvent.error, false); // Remove the `, false` if you're not trying to make a distinction
errorEvent.preventDefault();
});
window.addEventListener("unhandledrejection", errorEvent => {
handleError(errorEvent.reason, true); // Remove the `, true` if you're not trying to make a distinction
errorEvent.preventDefault();
});
You'd use wrapHandler when setting up your handlers, either directly:
btn.addEventListener("click", wrapHandler(async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
}));
...or by having another utility function:
function addListener(elm, eventName, fn) {
const handler = wrapHandler(fn);
return elm.addEventListener(eventName, handler);
return function() {
elm.removeEventListener(handler);
};
}
...then:
const removeBtnClick = addListener(btn, "click", async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
});
// ...if you want to remove it later...
removeBtnClick();
Live Example — since your original distinguished between synchronous errors and rejections, I've used that variant here, but again, its' really a distinction without a difference and I wouldn't distinguish them in my own code:
function handleError(err, isRejection) {
if (!(err instanceof Error)) {
err = Error(err);
}
output("error handler: " + err.message, isRejection ? "green" : "yellow");
}
window.addEventListener("error", errorEvent => {
handleError(errorEvent.error, false);
errorEvent.preventDefault();
});
window.addEventListener("unhandledrejection", errorEvent => {
handleError(errorEvent.reason, true);
errorEvent.preventDefault();
});
function wrapHandler(fn) {
return function(evt) {
try {
const result = fn(event);
Promise.resolve(result).catch(e => handleError(e, true));
} catch (e) {
handleError(e, false);
}
};
}
function addListener(elm, eventName, fn) {
const handler = wrapHandler(fn);
return elm.addEventListener(eventName, handler);
return function() {
elm.removeEventListener(handler);
};
}
function output(txt, color) {
const div = document.createElement("p");
div.textContent = txt;
if (color) div.style.backgroundColor = color;
document.body.appendChild(div);
}
const btn = document.createElement("button");
btn.innerHTML = "The button";
addListener(btn, "click", async evt => {
evt.stopPropagation();
output("The button was clicked");
noFunction(); // FIXME:
});
document.body.appendChild(btn);
const btn2 = document.createElement("button");
btn2.innerHTML = "With try/catch";
addListener(btn2, "click", async evt => {
evt.stopPropagation();
try {
output("Button 2 was clicked");
noFunction2(); // FIXME:
} catch (err) {
console.warn("catch", err)
throw Error(err);
}
});
document.body.appendChild(btn2);
new Promise(function(resolve, reject) {
setTimeout(function() {
return reject('oh noes');
}, 100);
});
justAnError();
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1">
I'm working with service worker to display notification between my users. In my code I include notificationclick event. With this event I'm trying to manage two cases. First case, if in my browser the page of my site is opening, don't open it but focus on it. Second case, if my browser don't show my site, open it and focus on it. But I haven't been succed...
Here is my current code:
self.addEventListener('notificationclick', function (e) {
console.log('notification was clicked')
var notification = e.notification;
var action = e.action;
if (action === 'close') {
notification.close();
} else {
// This looks to see if the current is already open and
// focuses if it is
e.waitUntil(
self.clients.matchAll().then(function(clientList) {
console.log(clientList)
if (clientList.length > 0) {
console.log(clientList[0])
return clientList[0].focus();
}
return self.clients.openWindow('/');
})
);
};
});
self.addEventListener("notificationclick", (event) => {
event.waitUntil(async function () {
const allClients = await clients.matchAll({
includeUncontrolled: true
});
let chatClient;
let appUrl = 'xyz';
for (const client of allClients) {
//here appUrl is the application url, we are checking it application tab is open
if(client['url'].indexOf(appUrl) >= 0)
{
client.focus();
chatClient = client;
break;
}
}
if (!chatClient) {
chatClient = await clients.openWindow(appUrl);
}
}());
});