Hello I have the following code:
function updateData() {
var element = document.getElementById('elementId');
element.remove(); ** //Here i want to remove my elements**
return firebase.database().ref(varurl3).child('UserCount/count').once('value').then(function(snapshot) {
var username = snapshot.val();
for (var i = 1; i <= username; i++) {
var ref = firebase.database().ref(varurl3).child('Users').child(i).child('User');
ref.once('value', function(snapshot) {
var changedPost = snapshot.val();
var log = document.createElement('div');
log.setAttribute('id', 'elementId');
log.style.height = '75px';
log.style.width = '300px';
log.style.overflow = 'auto';
log.style.border = '1px solid #666';
log.style.backgroundColor = '#ccc';
log.style.padding = '8px';
log.style.position = 'relative';
log.style.left = '1400px';
log.style.top = '300px';
log.style.margin = '10px';
log.textContent = changedPost;
document.body.appendChild(log)
});
}
});
}
(async function looper() {
while (true) { // forever
await updateData();
await delay(2000);
}
})(); // execute looper
My function is called every 2 seconds to retrieve values from my firebase database and write them into divs every 2 seconds. Therefore i create with a for loop as many divs as i have values in my database. When the function is called again from new all div elements with the same ID should be deleted, so that they don't duplicate on the next run.
But these div elements are not deleted and I get the error message:
Uncaught (in promise) TypeError: element is null
Could someone please help me how I can solve this problem?
Element Ids need to be unique, so there is no way to fetch all elements with the same ID in one call.
You could try setting your divs to have a common tag, and use getElementsByTagName to collect them all up for deletion.
This post may be a useful reference: Javascript: getElementById vs getElementsById (both works on different pages)
Related
I was just playing around and went to CNN article and ran this code in the console. The idea was to be able to click a p element and it would replace the DOM node with a node that has the reverse text in it. It works as expected but only once. When I try to run it again to reverse it back, I am hit with the error in console and I can't tell why:
TaggedEventTracker.js:56 Uncaught TypeError: Cannot read property 'dataset' of null
at e.findNearestZjsOrAElement (TaggedEventTracker.js:56)
at e.findNearestZjsOrAElement (TaggedEventTracker.js:63)
at e.trackTaggedEvent (TaggedEventTracker.js:73)
at HTMLDocument.<anonymous> (TaggedEventTracker.js:17)
CODE:
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
let nodeText = target.innerHTML;
let newEl = document.createElement('p');
newEl.innerHTML = [...nodeText].reverse().join("");
node.parentNode.replaceChild(newEl, node);
}
Why are you creating a new node to replace the old one? You can just replace the innerHTML of the original element.
When you replace the element, it looks like their event tracker got broken.
Also, by replacing the node, you also replace the event handler associated with it.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
target.innerHTML = [...target.innerHTML].reverse().join("");
}
<p>`123456789</p>
<p>`123456789</p>
<p>`123456789</p>
If you want to keep the original functionality and still replace the text, you will want to clone the node and attach a new event handler.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
let nodeText = target.innerHTML;
let newEl = target.cloneNode();
newEl.innerHTML = [...nodeText].reverse().join("");
node.parentNode.replaceChild(newEl, node);
newEl.onclick = function(evt) {updateText(evt.target)}
}
<p>12233</p>
<p>x3332233</p>
This is all you need if you want to swap text in it. Instead of using innerHTML use textContent.
let paragraphs = document.querySelectorAll('p');
paragraphs.forEach(para => {
para.onclick = function(evt) {
updateText(evt.target)
}
})
function updateText(target) {
let node = target;
node.textContent = [...target.textContent].reverse().join("");
}
I am trying to create a list of links using DOM nodes from the data in the database. Instead of having a link for each DOM element. The whole list only have one link. I want the link to be separate because I want to add value to it so I can identify which link the user clicks.
This is the link in HTML
This is my Javascript code in which I take data from database. I want to put each corresponding in database as the value for the corresponding link in the HTML code.
window.onload = async function outsong() {
var selected = localStorage.getItem("category")
document.getElementById("result").innerHTML = selected;
var result = [];
if(selected == "Popular") {
await db.collection("Song").doc("Popular").collection("songs").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
// doc.data() is never undefined for query doc snapshots
console.log(doc.id, " => ", doc.data());
result.push(doc.data());
});
});
console.log(result.length);
for(a = 0; a < result.length; a++) {
var node = document.createElement("li");
var textnode = document.createTextNode(result[a].song_name);
node.appendChild(textnode);
document.getElementById("songlist").appendChild(node);
var anchor = document.getElementById("songlist");
var att = document.createAttribute("value");
att.value = result[a].song_name;
anchor.setAttributeNode(att);
}
}
In the image, I want to separate the link, not one as the whole.
Within your loop, you'll need to create a new <a> element within each <li>, and add the song link to that a element. Modifying your code:
const songList = document.getElementById("songlist");
for(let a = 0; a < result.length; a++) {
var node = document.createElement("li");
var link = document.createElement('a');
link.setAttribute('href', result[a].song_link);
link.innerText = result[a].song_name;
node.appendChild(link);
songList.appendChild(node);
}
I don't see where you're getting the song_link from, so for the sake of the example, I guess it was in the result[a] object, alongside song_name.
I´m trying to loop through the content of a DataSnapshot and then depending on a condition do some work FOR EACH one of the elements but currently, the ForEach is only doing the work in the first item. The "serverStatus" sometimes is waiting and sometimes in "onCall". When the first item is "onCall" does not go through the rest of the items as I think is supposed to do. Below a snapchot of where I get the information from:
And here is my function:
exports.manageCallRequests = functions.database.ref('/resquests/{userId}').onCreate((snap, context) => {
const event = snap.val();
console.log("function manageCallRequests is being called")
var rootPath = admin.database().ref();
var userOnCall = context.params.userId;
var serversRef = rootPath.child('servers');
var callRequest = event;
var userTime = callRequest["time"];
var waiting= "waiting";
//We first get all the servers in ascending order depending on the last time they were used
var serversSorted = serversRef.orderByChild('lastTimeUsed')
//Gets the children on the "serversSorted" Query
return serversSorted.once("value").then(allServers =>{
//Checks if there is any child
if(allServers.hasChildren()){
allServers.forEach(async function(server) {
//we extract the value from the server variable, this contains all the information
//about each one of the servers we have
var serverInfo = server.val();
var serverKey = server.key;
var serverNumber = serverInfo["serverNumber"];
var serverStatus = serverInfo["serverStatus"];
console.log("server status "+serverStatus)
if(serverStatus === waiting){
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
}
});
}else{
console.log("No servers available")
}
});
});
I had the same behavior because my cloud function was exited before that all iterations were executed in the forEach loop.I get rid of it using this snippet of code:
for (const doc of querySnapshot.docs) {
// Do wathever you want
// for instance:
await doc.ref.update(newData);
}
I found 2 ways of getting this done. The first one is useful if we have a DataSnapshot without any OrderBy* call, in this case, would be:
var allServers = await serversRef.once("value");
for (let serverKey of Object.keys(allServers.val())){
var server = allServers[serverKey];
//Do some work
}
We need to first get the keys of the object to then be able to extract it from within the for loop, as explained here otherwise we´ll get a "TypeError: 'x' is not iterable"
Now the problem with this particular case is that a have a DataSnapshot that was previously sorted at var serversSorted = serversRef.orderByChild('lastTimeUsed') so when we call Object.keys(allServers.val()) the value returned is no longer sorted and that´s where forEach() comes in handy. It guarantees the children of a DataSnapshot will be iterated in their query order as explained here however for some reasons when doing some async work within the forEach loop this seems not to work, that´s why I had to do this:
var serversSorted = serversRef.orderByChild('lastTimeUsed')
var allServers = await serversSorted.once("value");
//Checks if there is any children
if (allServers.hasChildren()) {
//if there is iterate through the event that was passed in containing all
// the servers
var alreadyOnCall = false;
var arrayOfServers = []
var arrayOfKeys = []
allServers.forEach(function(individualServer){
arrayOfKeys.push(individualServer.key)
arrayOfServers.push(individualServer)
})
for (var serveIndex = 0; serveIndex < arrayOfServers.length;serveIndex++){
var serverObj = arrayOfServers[serveIndex]
var serverObject = serverObj.val()
var serverKey = arrayOfKeys[serveIndex]
var serverStatus = serverObject["serverStatus"];
var serverNumber = serverObject["serverNumber"];
console.log("server info "+serverStatus+" "+serverKey);
if (serverStatus === waiting && alreadyOnCall === false) {
const setCallRequest = await serversRef.child(serverKey).child("current").child("callRequest").set(callRequest);
const removeUserOnCall = await rootPath.child("resquests").child(userOnCall).remove();
const setServerStatus = await serversRef.child(serverKey).child("serverStatus").set("onCall");
alreadyOnCall= true
console.log("Call properly set");
}
}
}
I have variables which:
display the result (result), and
reference the current node (thisNode).
What do I need to change in my code so that it would display the HTML class?
var thisNode = document.body.firstChild;
var result = document.getElementById("resultOfButton");
result.InnerHTML = thisNode.;
/* Here, in JS are there any ways like displaying the class name,
like nodeClass */
Please give recommendations for my code. There may be some errors. Thank you.
var thisNode = document.body.firstChild;
var result = document.getElementById("resultOfButton");
var block = false;
function buttonDown()
{
if(block == true)
{
thisNode = thisNode.parentElement.firstChild;
block = false;
}
thisNode = thisNode.nextSibling;
result.innerHTML = thisNode.nodeName;
if(thisNode == thisNode.parentNode.lastChild)
{
block = true
}
}
function buttonUp()
{
// not done now...
}
function buttonEnter()
{
thisNode = thisNode.firstChild;
result.innerHTML = thisNode.c;
}
function buttonBack()
{
// not done now...
}
I think you're asking for the className attribute. I copied your first sample and added some code so you can run it on this page. You'll get the second emoji replaced by the class name of the inserted element.
var thisNode = document.getElementById("thisNode"); // document.body.firstChild;
var result = document.getElementById("resultOfButton");
result.innerHTML = thisNode.className; /*Here, in JS are there any ways like displaying the class name, like nodeClass*/
<div id="thisNode" class="sample-class">🙂</div>
<div id="resultOfButton">🙃</div>
Quoting MDN:
"The className property of the Element interface gets and sets the value of the class attribute of the specified element."
I'm trying to dynamically generate chat but the messages get inserted below the previous meaning the user has to scroll down to see a new message which isn't very good! I'm trying to change it so the message gets inserted above the last message.
var elementDiv = document.createElement("div");
elementDiv.className = "chat-message error";
elementDiv.appendChild(document.createTextNode(chatBoxMessage));
//Insert message
var oldMsg = "chat-message";
document.getElementsByClassName('message-window').appendChild(elementDiv);
document.body.insertBefore(elementDiv, oldMsg);
The variable chatBoxMessage is used to define the text.
Running this throws the error -
Uncaught TypeError: Failed to execute 'insertBefore' on 'Node' : parameter 2 is not of type 'Node'.
Seems like oldMsg is a string type. You'll have to call document.createTextNode with the oldMsg in order for it to work.
I made a sample implementation. Hopefully this is what you are looking for:
HTML:
<div id='container'></div>
Javascript:
// select the container
var container = document.getElementById('container');
// create chat messages
var msg1 = createChatMsg('Chat1');
var msg2 = createChatMsg('Chat2');
var msg3 = createChatMsg('Chat3');
// Insert Messages
container.insertBefore(msg1, container.firstChild);
container.insertBefore(msg2, container.firstChild);
container.insertBefore(msg3, container.firstChild);
// create chatmessage
function createChatMsg(message) {
var chatMsg = document.createElement('div');
chatMsg.className = 'some-chat';
chatMsg.appendChild(document.createTextNode(message));
return chatMsg;
}
Hopefully this helps and is what you are looking for. Sample implementation: http://jsfiddle.net/f1d02e49/
You may need to wrap your message in a text node.
document.createTextNode("chat-message");
Your error message indicates that your text is not a valid node.
parameter 2 is not of type 'Node'
See: MDN - Document.createTextNode()
onReady('#message-window', function() {
pushNewMessage('Third!');
pushNewMessage('Second!');
pushNewMessage('First!');
});
function pushNewMessage(chatBoxMessage) {
var errorMessageDiv = document.createElement('div');
errorMessageDiv.className = 'chat-message error';
errorMessageDiv.appendChild(document.createTextNode(chatBoxMessage));
//Insert message
var messageWindow = document.getElementById('message-window');
messageWindow.insertBefore(errorMessageDiv, messageWindow.firstChild);
}
// Convinience function -- Wait for element or body load...
function onReady(selector, callback, ms) {
var intervalID = window.setInterval(function() {
if ((document.querySelector(selector) || document.body) !== undefined) {
window.clearInterval(intervalID);
callback.call(this);
}
}, ms || 500);
}
<div id="message-window">
</div>