I am trying to create an HTML page where DOM element will automatically update when there are new comments added to the database.
Here is the HTML code
<h1 id="name" style="text-align: center"></h1>
<h2 id="singername" style="text-align: center"></h2>
<textarea id="content"></textarea>
<br />
<button type="submit" id="submitreview" onclick="submitreview()">submit</button>
Here is the Javascript code to add review to the database when click submit:
async function submitreview() {
var selectedone = localStorage.getItem("category");
var selectedtwo = localStorage.getItem("song");
const reviewcontent = document.getElementById("content");
var d = new Date();
e = d.toLocaleString();
g = userID;
namelist = [];
await db.collection("User").doc(g).get().then(function(doc3) {
if (doc3.exists) {
console.log(doc3.data().name_user);
namelist.push(doc3.data());
} else {
console.log("nothing here exist");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
var username = namelist[0].name_user;
db.collection("Song").doc(selectedone).collection("songs").doc(selectedtwo).collection("reviews").doc().set({
review_content: reviewcontent.value,
review_date: e,
user_id: userID,
user_name: username,
})
document.getElementById("content").value = "";
}
Here is the javascript code to load DOM elements base on the data from database:
async function foo() {
var selectedone = localStorage.getItem("category");
var selectedtwo = localStorage.getItem("song");
var contentresult = [];
await db.collection("Song").doc(selectedone).collection("songs").doc(selectedtwo).collection("reviews").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc2) {
console.log(doc2.id, " => ", doc2.data());
contentresult.push(doc2.data());
// console.log(doc2.data()[1].review_date.todatestring());
});
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
console.log(contentresult);
const reviewlist = document.getElementById("reviewlist");
for(let b = 0; b < contentresult.length; b++) {
var node = document.createElement('div');
var size = document.createElement('h2');
var size2 = document.createElement('h2');
var size3 = document.createElement('p');
var breakpoint = document.createElement('br');
var anotherbreakpoint = document.createElement('br');
size.setAttribute('class','left');
size2.setAttribute('class','right');
var text1 = document.createTextNode(contentresult[b].user_name + ' - ');
var text2 = document.createTextNode('- ' + contentresult[b].review_date);
var text3 = document.createTextNode(contentresult[b].review_content);
size.appendChild(text1);
size2.appendChild(text2);
size3.appendChild(text3);
node.appendChild(size);
node.appendChild(size2);
node.appendChild(breakpoint);
node.appendChild(anotherbreakpoint);
node.appendChild(size3);
reviewlist.appendChild(node);
}
}
Right now, I have to reload the page every time I add a comment to see the updates on the page, how can I make it synchronize with the updates from the database. I use Firestore as the database.
There's no real time fetching going on with this code. If you add an listener, it will work. But there is another way. Just call function foo in the end of submitreview. I think it'll be enough. Happy to help :)
Related
I am trying to get the details of the the movie using tmdb api so fall everything was good until i tried to display all the genres of that movie
const tmdb_api_url = "https://api.themoviedb.org/3/tv/" + 127332 + "?api_key=API-KEY";
async function getDetails() {
const response = await fetch (tmdb_api_url);
const data = await response.json();
const { name, first_air_date, vote_average, number_of_seasons, episode_run_time, genres, overview, origin_country} = data;
document.getElementById('title').textContent = name;
document.getElementById('first_air_date').textContent = first_air_date;
document.getElementById('vote_average').textContent = vote_average;
document.getElementById('number_of_seasons').textContent = number_of_seasons + " Season" + (number_of_seasons == 1 ? "" : "s ");
document.getElementById('run_time').textContent = episode_run_time;
document.getElementById('overview').textContent = overview;
document.getElementById('origin_country').textContent = origin_country;
var g = "";
for (i in genres) {
g += genres[i].name;
}
document.getElementById('genres').textContent = genres[i].name;
}
getDetails();
This is what i tried but its only showing one genre
And can anyone help me simplify the code what i tried
You are getting only one genre because of this line document.getElementById('genres').textContent = genres[i].name;
This will always display the last genre.
Modify it as follows
var g = "";
for (i in genres) {
g += genres[i].name + ", ";
}
document.getElementById('genres').textContent = g.substr(0, g.length - 1); // to remove last , added
Update : Adding each genre in there respective anchor tag
let genreTags = "";
for(i in genres){
genreTags+= `${genres[i].name}`; // Add genre link in the href
}
document.getElementById("genres").innerHTML = genreTags;
This is the jist of adding genres as anchor, but there are much better ways of doing it.
So I built an app where you enter the name of your favorite book and author in a form, and it generates a card with that information.
There's a checkbox on the form to mark if you own it and a "remove" button.
It works with an addtoLibrary() function that takes the info from the form, puts it into an object and that object into firebase.
Within that function, I have another function called render() which takes the info from firebase and puts it into the DOM.
Next I added another function called retrievefromDatabase() which also incorporates the render() function, and loops through the list of objects to draw cards for the items that exist in the database.
The problem I'm facing now is that whenever the checkbox is checked or unchecked, the retrievefromDatabase() function activates and draws another card.
I think if I could change the on in the second database call to once here, that would solve my problem. But when I do that, all the fields in my card render to "undefined."
I'm not sure why it's even doing that, because the initial database call is a once that loops over every object key, and there are no new object keys when the checkbox changes. It's just a change of state for the object.
function retrievefromDatabase() {
firebase.database().ref("Book").once("value", gotData);
function gotData(Book) {
var books = Book.val();
var keys = Object.keys(books);
for (var i = 0; i < keys.length; i++) {
firebase.database().ref("Book/" + keys[i]).on("value", function(snapshot) {
newPostKey = snapshot.key;
function oldBook(title, fname, lname, pubDate, contrib, own) {
this.title = title;
this.fname = fname;
this.lname = lname;
this.pubDate = pubDate;
this.contrib = contrib;
this.own = own;
};
var archiveBook = new oldBook(snapshot.val().title,
snapshot.val().fname,
snapshot.val().lname,
snapshot.val().pubDate,
snapshot.val().contrib,
snapshot.val().own);
render();
})}}};
When you add a listener to a location in the database, you get a snapshot with all data under that location. This means that there is no need to attach a listener to nodes under that location in the callback.
In addition, it is much easier to loop over the child nodes of a snapshot with snapshot.forEach(), instead of extracting the keys and then using those.
So your code can be simplified to:
function retrievefromDatabase() {
firebase.database().ref("Book").once("value", (book) => {
book.forEach((snapshot) => {
newPostKey = snapshot.key;
function oldBook(title, fname, lname, pubDate, contrib, own) {
this.title = title;
this.fname = fname;
this.lname = lname;
this.pubDate = pubDate;
this.contrib = contrib;
this.own = own;
};
var archiveBook = new oldBook(snapshot.val().title,
snapshot.val().fname,
snapshot.val().lname,
snapshot.val().pubDate,
snapshot.val().contrib,
snapshot.val().own);
})
render();
}
};
On on vs once:
if you only want to get the data once, you should use once. This also means that you'll have to call retrievefromDatabase each time you want to load the data.
if you want to continue to monitor the database for changes after retrieving the data, you should use on. In this case, be sure to only call retrievefromDatabase once, as you don't want to attach multiple permanent listeners.
This is what I attempted, which worked, but is much uglier than Frank's solution:
function retrievefromDatabase() {
firebase.database().ref("Book").once("value", gotData);
function gotData(Book) {
var books = Book.val();
var keys = Object.keys(books);
for (var i = 0; i < keys.length; i++) {
firebase.database().ref("Book/" + keys[i]).once("value", function(snapshot) {
titlesnap = snapshot.val().title;
fnamesnap = snapshot.val().fname;
lnamesnap = snapshot.val().lname;
pubsnap = snapshot.val().pubDate;
contribsnap = snapshot.val().contrib;
newPostKey = snapshot.key;
const bookContainer = document.createElement("div");
bookContainer.classList.add("book-container");
const newVolume = document.createElement("div");
newVolume.classList.add("volume");
bookContainer.appendChild(newVolume);
bookContainer.setAttribute('id', `${newPostKey}`);
const frontCover = document.createElement("div");
newVolume.appendChild(frontCover);
frontCover.style.setProperty("background-color", getRandomColor());
frontCover.innerHTML = "<br /><br />"
+ "<b>" + titlesnap + "</b>" + "<br /><br />"
+ fnamesnap + " "
+ lnamesnap + "<br /><br />"
+ "Published: " + pubsnap + "<br />"
+ "Added by: <br />" + contribsnap + "<br />";
const checkbox = document.createElement('input');
checkbox.type = "checkbox";
checkbox.id = "checkbox";
if (snapshot.val().own == true) {
checkbox.checked = true;
}
else {
checkbox.checked = false;
};
checkbox.addEventListener("change", function() {
if (checkbox.checked === false) {
firebase.database().ref('Book/' + bookContainer.id + '/own').set(false);
}
else if (checkbox.checked === true) {
firebase.database().ref('Book/' + bookContainer.id + '/own').set(true);
}});
const label = document.createElement("label");
label.appendChild(document.createTextNode(" I own a copy"));
const newgraf = document.createElement("p")
frontCover.appendChild(checkbox);
frontCover.appendChild(label);
frontCover.appendChild(newgraf);
const removeButton = document.createElement('button')
frontCover.appendChild(removeButton);
removeButton.textContent = 'Remove';
removeButton.addEventListener("click", function(event){
firebase.database().ref('Book/').child(bookContainer.id).remove()
bookContainer.remove();
})
libraryContainer.insertAdjacentElement('afterbegin',bookContainer);
});
};
};};
I'm trying to make my script only log the strings once. So when a new string is created it will only log the new strings... This script logs all of the strings at once. I would like to find a way to make it only log the .innerText of freshly created children in that element with the id "team_log_actual".
Children can be added at any point of time.
Here is my script:
setInterval(function() {
var logelement = $('#team_log_actual');
var foo = document.getElementById('team_log_actual');
for (let i = 0; i < foo.children.length; i++) {
//console.log(foo.children[i].innerText);
var PRINTactions = foo.children[i].innerText;
var PRINTactionsEle = foo.children[i];
var fields = PRINTactions.split(/ /);
var Username = fields[2]; //grabs the second field from the splitted string. which is the username..
var text1 = fields[3];
var text2 = fields[4];
var text3 = fields[5];
var text4 = fields[6];
var text5 = fields[7];
var text6 = fields[8];
var text7 = fields[9];
var text8 = fields[10];
var text9 = fields[11];
var text10 = fields[12];
//Defined multiple just in case. Each field will contain a string. If one is there, but it's highly unlikely to go past 5-6.
var combinetext = fields[3] + " " + fields[4] + " " + fields[5] + " " + fields[6] + " " + fields[7];
if (combinetext.includes("used item") == true) {
console.log(Username + " : " + combinetext) // log the Username and the combinedtext. Only if the combinetext includes the string "in ejector".
}
}
}, 10000); // Run every 10 seconds.
Solved using MutationObserver. Recommended by user: CodeWalker
// Solved code.
var Logconsole = document.getElementById('team_log_actual');
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
for (var i = 0; i < mutation.addedNodes.length; i++) {
//This var gets the innertext of the new element.
var TextFromNewElement = mutation.addedNodes[i].innerText;
console.log(TextFromNewElement);
}
});
});
observer.observe(Logconsole, {
childList: true
});
//Test code. ----- Create a fake element every 5 seconds.
var testelement = document.createElement('div');
testelement.id = 'team_log_actual';
testelement.innerHTML = '<p> <b>[<span style="color: #0FF">USER:</span>] <bdi style="color: #0FF;">User2</bdi></b> harvest a plant.</p>'
setInterval(function(){
//every 5 seconds, add the test element.
Logconsole.appendChild(testelement)
}, 5000);
<div id="team_log_actual" class="log">
<p> <b>[<span style="color: #0FF">USER:</span>] <bdi style="color: #0FF;">User1</bdi></b> planted a seed.</p>
</div>
I filtered a lookup field by current user id and status using "SP.js CSOM SP.ClientContext" and it was successful in filtering the lookup. But trying to save gave an error "Sorry, Something went wrong", with a correlation ID "9b40ec9e-1004-8000-bbff-36490b07f284".
Note: if i take off the filtering, it saves just fine.
I tried using other methods of filtering, using CAML queries and JSOM Ajax calls. All filtering were successful but saving didn't work.
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', sharePointReady);
function sharePointReady(){
getItemsFromView("Activity Plan", "Approved",
function(items){
var field = getField("select", "My Planned Tasks")
for(var i = 0; i < items.get_count(); i++){
var item = items.get_item(i);
var node = document.createElement("option");
var textnode = document.createTextNode(item.get_item('Deliverable'));
node.appendChild(textnode);
field.appendChild(node);
}
},
function(sender,args){
console.log(args.get_message())
}
);
}
function getItemsFromView(listTitle, viewTitle,success,error){
var ctx = new SP.ClientContext();
var list = ctx.get_web().get_lists().getByTitle(listTitle);
var view = list.get_views().getByTitle(viewTitle);
ctx.load(view,'ViewQuery');
ctx.executeQueryAsync(
function() {
var viewQry = "<View><Query>" + view.get_viewQuery() + "</Query>
</View>";
getItems(listTitle,viewQry,success,error);
},
error);
}
function getItems(listTitle, queryText,success,error) {
var ctx = new SP.ClientContext();
var list = ctx.get_web().get_lists().getByTitle(listTitle);
var query = new SP.CamlQuery();
query.set_viewXml(queryText);
var items = list.getItems(query);
ctx.load(items);
ctx.executeQueryAsync(
function() {
success(items);
},
error
);
}
function getField(fieldType,fieldTitle) {
var docTags = document.getElementsByTagName(fieldType);
for (var i=0; i < docTags.length; i++) {
if (docTags[i].title == fieldTitle) {
while (docTags[i].firstChild) {
docTags[i].removeChild(docTags[i].firstChild);
}
return docTags[i];
}
}
return false;
}
i expect it save on the calendar event list but i got the error message "Sorry, something went wrong" and a correlation id "9b40ec9e-1004-8000-bbff-36490b07f284"
you did not put a value for the option. So it is like an Option without an ID and just Text so need to replace this section
var textnode = document.createTextNode(item.get_item('Deliverable'));
node.appendChild(textnode);
with the following
var textnode = document.createTextNode(item.get_item('Deliverable'));
node.value = item.get_item('ID');
node.appendChild(textnode);
I am writing a Javascript dialog script which is seen in a lot of typical Role Playing Games.alt text http://www.dailynintendo.com/wp-content/uploads/2008/12/luminous-arc-2-dialogue.jpg
At the moment I got an array with text strings which you can skip trough. I got at the point where you can make a decision and based on the input a different string will show.
However I don't think this is the right way to do it. These are the requirements for the script:
Support for multiple dialog scripts
multiple characters
user decision input ("Do you like me?" -yes -no)
This is my code at the moment:
// Intro script
var script_intro = [];
script_intro[0] = 'Hello how are you?';
script_intro[1] = 'So I heard..';
script_intro[2] = 'This is a cool game!';
script_intro[3] = [];
script_intro[3][0] = 'Do you like me?';
script_intro[3][1] = [];
script_intro[3][1][0] = 'Jah';
script_intro[3][1][1] = 4;
script_intro[3][2] = [];
script_intro[3][2][0] = 'Nah';
script_intro[3][2][1] = 5;
// Intro script: variation I
var script_intro_1 = [];
script_intro_1[0] = 'I love you too!';
// Intro script: variation II
var script_intro_2 = [];
script_intro_2[0] = 'Damn you...';
function initDialog()
{
// This is where the text will be shown
var dialog = document.getElementById('dialog');
var content = document.getElementById('content');
var nextButton = document.getElementById('nextButton');
var optionButton_1 = document.getElementById('optionButton_1');
var optionButton_2 = document.getElementById('optionButton_2');
// How fast the characters will appear after each other (milliseconds)
var scrollSpeed = 50;
}
// Scroll text per line, character
function scrollText(script, line)
{
var char = 0;
// If this line contains a question that requires user input
if(typeof(script[line]) == 'object')
{
var textScroller = setInterval(
function()
{
// Add the string char for char
content.innerHTML += script[line][0][char];
char ++;
if(char >= script[line][0].length)
{
clearInterval(textScroller);
// Show options
options(script, line);
}
}, scrollSpeed);
}
else
{
var textScroller = setInterval(
function()
{
content.innerHTML += script[line][char];
char++;
if(char >= script[line].length)
{
clearInterval(textScroller);
// Show next line
next(script, line);
};
}, scrollSpeed);
}
}
function next(script, line)
{
line = line + 1;
// Last line has been shown
if(script[line] == undefined)
{
//alert('End of dialog');
}
else
{
nextButton.style.visibility = 'visible';
nextButton.onclick = function()
{
nextButton.style.visibility = 'hidden';
content.innerHTML = '';
scrollText(script, line);
}
}
}
function options(script, line)
{
optionButton_1.innerHTML = script[line][1][0];
optionButton_2.innerHTML = script[line][2][0];
optionButton_1.style.visibility = 'visible';
optionButton_2.style.visibility = 'visible';
optionButton_1.onclick = function()
{
optionButton_1.style.visibility = 'hidden';
optionButton_2.style.visibility = 'hidden';
content.innerHTML = '';
scrollText('script_intro_1', 0);
}
optionButton_2.onclick = function()
{
optionButton_1.style.visibility = 'hidden';
optionButton_2.style.visibility = 'hidden';
content.innerHTML = '';
scrollText('script_intro_2', 0);
}
}
html
<body onload="scrollText(script_intro, 0)">
<h1>rpg</h1>
<a id="reset" href="#">Reset</a>
<div id="device">
<div id="dialog">
<strong>NPC:</strong>
<div id="content"></div>
<a id="nextButton" href="#">Next</a>
<a id="optionButton_1" href="#"></a>
<a id="optionButton_2" href="#"></a>
</div>
</div>
</body>
I could really use some feedback. What is the best way to write such script with the requirements above? Is using JSON or XML a better option than an Array for the dialog scripts?
I especially need some hints on how to implement multiple choices in the script.
Thank you!
If this is a script that has a scripted flow to it, I would use the state machine pattern.
http://www.eventhelix.com/RealtimeMantra/HierarchicalStateMachine.htm
There are tons of links, I just grabbed the first I searched from google. What I would do is have a state for each situation the user will presented with options. Each option would be a transition to another state. So for instance
function State(questionText){
this.transitionsOut = [];
this.questionText = questionText;
}
State.prototype = {
transitionsOut:null,
questionText:null,
}
function Transition(startState, endState, optionText){
startState.transitionsOut[startState.transitionsOut.length] = this;
this.start = startState;
this.end = endState;
}
Transition.prototype = {
start:null,
end:null,
optionText:null
}
Then what you can do, is make your state machine, and then for the current state, print out your State Message, then underneath list each option for that state.
var startState = new State('Where do you want to go');
var north = new State('North');
var south = new State('South');
var transition1 = new Transition(startState,north,'Lets go north');
var transition2 = new Transition(startState,south,'Lets go south');
The code to then display what is in the current state, and the options is trivial, as is the transition from one state to another based on what the user picks.