I have a list of cats. Each cat object has a property cat.clicks that record the number of times the cat image has been clicked. The cat image's onclick calls the method cat.clickCat.
But of course 'this' in the clickCat method refers to the image element and not the cat object which contains the property 'clicks'.
How do I display and update the number of clicks on the image?
function Cat(src, name) {
this.src = src;
this.name = name;
this.clicks = 0; //property recording no. of clicks
}
Cat.prototype.createCatItem = function() {
let catDisplay = document.createElement("div");
catDisplay.id = "catDisplay"
let catName = document.createElement("h2");
let catImg = document.createElement("img");
let catCounter = document.createElement("div");
catCounter.id = "clicker";
catName.innerHTML = this.name;
catImg.src = this.src;
catImg.onclick = this.clickCat; //call the clickCat method
catDisplay.appendChild(catName);
catDisplay.appendChild(catImg);
catDisplay.appendChild(catCounter);
return catDisplay;
}
Cat.prototype.clickCat = function() {
this.clicks += 1; //how to access the object property clicks from this method?
let clickerDiv = document.getElementById("clicker")
clickerDiv.innerHTML = ''
clickerDiv.innerHTML = 'clicks = ' + this.clicks;
}
function App() {
this.cats = [];
}
App.prototype.add = function(cat) {
this.cats.push(cat)
}
App.prototype.listCats = function() {
let container = document.getElementById("container");
let ul = document.createElement("ul");
for (let i=0; i<this.cats.length; i++){
let li = document.createElement("li");
li.innerHTML = this.cats[i].name;
li.onclick = this.displayCat;
ul.appendChild(li);
}
container.appendChild(ul);
}
App.prototype.displayCat = function() {
let container = document.getElementById("container");
let catDisplay = document.getElementById("catDisplay")
let cats = app.cats;
let chosenCat = cats.filter(cat => cat.name === this.innerHTML);
let chosenCatItem = chosenCat[0].createCatItem();
container.removeChild(catDisplay);
container.appendChild(chosenCatItem);
console.log(chosenCat);
}
App.prototype.showFirstCat = function() {
let container = document.getElementById("container");
let catDisplay = document.getElementById("catDisplay")
let firstCat = app.cats[0].createCatItem();
container.appendChild(firstCat);
}
let app = new App;
let tea = new Cat("http://placehold.it/350x150", "tea");
let snowball = new Cat("http://placehold.it/350x200", "snowball");
let triksy = new Cat("http://placehold.it/350x300", "triksy");
let vera = new Cat("http://placehold.it/350x350", "vera");
let jon = new Cat("http://placehold.it/350x400", "jon");
app.add(tea)
app.add(snowball)
app.add(triksy)
app.add(vera)
app.add(jon)
app.listCats();
app.showFirstCat();
<div id="container">
<h1>My Cat Clicker</h1>
</div>
First of all .. beware of using this ... this always refer the object currently responsible to execute the scripts in browser ... so in your case the image is responsible for executing the click event and it has no property called clicks .. and that's why clicks is NaN
A good practice is preserve this into a variable to avoid the system replacement when executing (that=this)
Cat.prototype.createCatItem = function() {
let that=this; //preserving this value
let catDisplay = document.createElement("div");
catDisplay.id = "catDisplay"
let catName = document.createElement("h2");
let catImg = document.createElement("img");
let catCounter = document.createElement("div");
catCounter.id = "clicker";
catName.innerHTML = this.name;
catImg.src = this.src;
catImg.onclick = function() {
//alert(this);
that.clicks += 1; //how to access the object property clicks from this method?
let clickerDiv = document.getElementById("clicker")
clickerDiv.innerHTML = ''
clickerDiv.innerHTML = 'clicks = ' + that.clicks;
}
catDisplay.appendChild(catName);
catDisplay.appendChild(catImg);
catDisplay.appendChild(catCounter);
return catDisplay;
}
//Cat.prototype.clickCat = function() {
// this.clicks += 1; //how to access the object property clicks from this method?
// let clickerDiv = document.getElementById("clicker")
// clickerDiv.innerHTML = ''
// clickerDiv.innerHTML = 'clicks = ' + this.clicks;
//}
Related
I have a div with a class called post, and I am iterating through a list of posts from the backend that I want to display on the front end.
My requirement is that I first want to create an empty div and append all the created elements in that div, and then finally push that div in the <div class = 'post'. But for some reason, it's giving me an error saying appendChild is not a function.
It would be great if I could convert this empty div element to look like below just through javascript. Since I want to style each of my posts and so I am wrapping them in a div.
EDIT: Below is my javascript code that I tried
for (let i = 0; i < paginatedItems.length; i++) {
let post_wrapper = document.createElement('div');
let post_element = document.querySelector('.post');
let hr = document.createElement('hr');
// Title of the blog
let title_element = document.createElement('h2')
title_element.classList.add('mt-4');
title_element.innerHTML = paginatedItems[i].title;
post_element.appendChild(title_element);
// Image of the Blog
let image_element = document.createElement('img');
image_element.classList.add('img-fluid');
image_element.classList.add('rounded');
image_element.style.width = '672'
image_element.style.height = '372'
image_element.src = paginatedItems[i].featured_image;
post_element.appendChild(image_element);
// Author Element
let author_element = document.createElement('p');
author_element.classList.add('lead');
author_element.innerHTML = 'By ';
let author_link = document.createElement('a')
author_link.innerHTML = paginatedItems[i].author.name;
author_link.href = 'google.com'
author_element.appendChild(author_link);
author_link.appendChild(hr);
post_element.appendChild(author_element);
// // Date Element
let date_element = document.createElement('p');
date_element.classList.add('item');
date_element.innerHTML = `Posted ${timeSince(paginatedItems[i].date)} ago`;
post_element.appendChild(date_element);
date_element.appendChild(hr);
// Description Element
let description_element = document.createElement('p');
description_element.classList.add('item');
description_element.innerHTML = paginatedItems[i].content.substr(0, 300) + '....';
post_element.appendChild(description_element);
// Show more button
let input_button = document.createElement('a')
input_button.classList.add('btn-primary');
input_button.classList.add('btn');
input_button.textContent = "Show more..";
input_button.addEventListener('click',
function () {
RenderPost(paginatedItems[i].ID);
}
)
console.log(post_element);
post_wrapper.appendChild(post_element);
post_element.appendChild(input_button);
}
you have to use forEach method.
here is an example:
const postsArray = [{title: 'miaw', id: 123324}, {title: 'hello', id: 983745}];
// the dom div you want to append to.
const myDiv = document.getElementById('[yourDivId]');
postsArray.forEach(post=>{
// creating the post
var div = document.createElement('div');
var title = document.createElement('h1');
var id = document.createElement('h4');
title.textContent = post.title;
id.textContent = post.id;
// appending the elements to a div
div.append(title, id);
// then appending the post to your div
myDiv.appendChild(div);
});
Getting element by id fixed it for me
here is the final working code snippet
let post_element = document.querySelector('#posts');
for (let i = 0; i < paginatedItems.length; i++) {
let post_wrapper = document.createElement('div');
let hr = document.createElement('hr');
// Title of the blog
let title_element = document.createElement('h2')
title_element.classList.add('mt-4');
title_element.innerHTML = paginatedItems[i].title;
post_wrapper.appendChild(title_element);
// Image of the Blog
let image_element = document.createElement('img');
image_element.classList.add('img-fluid');
image_element.classList.add('rounded');
image_element.style.width = '672'
image_element.style.height = '372'
image_element.src = paginatedItems[i].featured_image;
post_wrapper.appendChild(image_element);
post_element.appendChild(post_wrapper);
}
This is my JS that adds evenet listeners to every ideanode:
var ideanodes = [...document.querySelectorAll('.ideanode')];
ideaNodesListRefresh();
function ideaNodesListRefresh(){
ideanodes = [...document.querySelectorAll('.ideanode')];
console.log("refreshed")
ideanodes.forEach(ideanode => {
var maintxt = ideanode.querySelector(".maintext");
var titleArrow = ideanode.querySelector(".title-arrow");
var mainArrow = ideanode.querySelector(".maintxt-arrow");
var comments = ideanode.querySelector(".comments");
titleArrow.addEventListener('click', function() {
maintxt.classList.toggle("hidden");
mainArrow.classList.toggle("hidden");
if (comments.classList.contains("hidden")) {;
} else {
comments.classList.toggle("hidden");
};
});
mainArrow.addEventListener("click", function() {
comments.classList.toggle("hidden");
});
});
};
the function gets fired at the end of the creation of a new ideanode:
function createNeed(user) {
if (user==user1) {
var newNeed = document.createElement('div');
newNeed.className = "ideanode";
var newNeedHeader = document.createElement('div');
newNeedHeader.className = "ideanodeheader";
var newNeedHeaderText = document.createTextNode('Need');
newNeedHeader.appendChild(newNeedHeaderText);
newNeed.appendChild(newNeedHeader);
var newNeedContent = document.createElement('div');
newNeedContent.className = "content";
newNeed.appendChild(newNeedContent);
var needTitle = document.createElement('div');
needTitle.className="title";
var needTitleH3 = document.createElement('h2');
var titleText = document.createTextNode('Title');
needTitleH3.appendChild(titleText);
needTitleH3.setAttribute('onclick', 'this.focus();');
needTitleH3.setAttribute('contenteditable', 'True');
newNeedContent.appendChild(needTitleH3);
var downArrow0 = document.createElement('i');
downArrow0.classList = 'fas fa-sort-down title-arrow';
newNeedContent.appendChild(downArrow0);
var maintext = document.createElement('div');
maintext.classList = 'maintext hidden';
textareaMain = document.createElement("textarea");
textareaMain.className = "maintextinput";
textareaMain.setAttribute('placeholder', 'Text');
maintext.appendChild(textareaMain);
newNeedContent.appendChild(maintext);
var downArrow1 = document.createElement('i');
downArrow1.classList = 'fas fa-sort-down maintxt-arrow hidden';
newNeedContent.appendChild(downArrow1);
var comments = document.createElement('div');
comments.classList = "comments hidden";
textareaComments = document.createElement("textarea");
textareaComments.className = "commentsinput";
textareaComments.setAttribute('placeholder', 'Comments');
comments.appendChild(textareaComments);
newNeedContent.appendChild(comments);
newNeed.appendChild(newNeedContent);
var container = document.querySelector('#needsuser1');
var lastNeed = document.querySelector(".ideanode:last-child");
lastNeed.parentNode.insertBefore(newNeed, lastNeed.nextSibling);
ideaNodesListRefresh();
} else {
console.log("user2")
}
}
But when I add a new ideanode the function doesn't work properly and the eventlisteners only work for the newest ideanode.
This is a codepen of what I'm doing: https://codepen.io/ricodon1000/pen/PoPeXJL
I would like to simply add eventlisteners to the arrows of the new ideanode and have all of the arrows of all of the ideanodes work.
I have been trying to create an app named workspace. I had asked another question earlier but now the features I have added are more. There is a remarks system. I have tried using different versions of my code and the code I have given has the best version I created. I cannot find an answer to my question on the net so I had to ask it here.
var ref = firebase.database().ref();
function stdRemarks(studentName){
let finalStuff;
ref.on("value", function(snapshot){
let keys = Object.keys(snapshot.val().schools[returnCurrentUser()][studentName]['remarks']);
for(i=0;i<keys.length;i++){
let objectToDealWith = snapshot.val().schools[returnCurrentUser()][studentName]['remarks'];
let remark = objectToDealWith[keys[i]]['remark'];
let examiner = objectToDealWith[keys[i]]['examiner'];
let fullRemark = ` ${examiner}: ${remark} | `
finalStuff += fullRemark;
}
return finalStuff;
});
}
ref.on("value", function(snapshot){
let dashTab = document.getElementById("dashboard_table");
let btn = document.getElementById("csv_blob");
let btn2 = document.getElementById("json_view");
let btn3 = document.getElementById("json_export");
btn.style.display = "block";
btn2.style.display = "block";
$("#json_export").css('display', 'block');
dashTab.innerHTML = "<thead><tr><th>Student Name</th><th>Class</th><th>Email</th><th>Subject</th><th>Project Info</th><th>Remarks</th><th>Project</th><th style='display: none;'>Project Download URL</th><th>Add Remark</th></tr></thead>";
let jsonRecieved = snapshot.val();
let objectToDealWith = snapshot.val().schools[returnCurrentUser()];
let lengthOfIt = Object.size(objectToDealWith);
for(i=0;i<lengthOfIt;i++){
let int = i + 1;
let names = Object.keys(objectToDealWith);
let stdName = names[i];
let finalResult = objectToDealWith[stdName];
document.getElementById("schoolnameis").innerText = "Dashboard - " + objectToDealWith['i'];
let stdClass = finalResult['class'];
let stdEmail = finalResult['email'];
let stdSubject = finalResult['subject'];
let stdiName = finalResult['stdname'];
let stdProjectName = finalResult['projectname']
let stdProjectInfo = finalResult['projectinfo'];
let stdProjectLink = finalResult['projectlink'];
console.log(stdRemarks(stdiName))
let elementToPush = `<tr><td>${stdiName.replace(/undefined/g, '')}</td><td>${stdClass.replace(/undefined/g, '')}</td><td>${stdEmail.replace(/undefined/g, '')}</td><td>${stdSubject.replace(/undefined/g, '')}</td><td>${stdProjectInfo.replace(/undefined/g, '')}</td><td>${stdRemarks(stdnameName).replace(/undefined/g, '')}</td><td><a href=${stdProjectLink}>${stdProjectName.replace(/undefined/g, '')}</a></td><td style='display:none;'>${stdProjectLink}</td><td id="${stdName}" style='text-align:center;' onclick="closeThatSomeThing();getIdOfTd(this.id)">➕</td></tr>`;
dashTab.innerHTML += elementToPush;
}
});
So everything is working fine but some stuff here seems to corrupt the whole code. My database looks somewhat like this
Here is the error.
//A warning by firebase.
#firebase/database: FIREBASE WARNING: Exception was thrown by user callback. TypeError: Cannot read property 'replace' of undefined
//An error occuring on the variable `elementToPush` and its part ${stdRemarks(stdnameName).replace(/undefined/g, '') in the code.
Cannot read property 'replace' of undefined
I have to submit this project tomorrow.
your function 'stdRemarks' has a return type of 'void'. either return the the complete ref.on() or move 'finalstuff' outside the .on() function call and make sure the function 'stdRemarks' has the desired return type. in this case this would be a 'string';
function stdRemarks(studentName){
let finalStuff = "";
ref.on("value", function(snapshot){
let keys = Object.keys(snapshot.val().schools[returnCurrentUser()][studentName]['remarks']);
for(i=0;i<keys.length;i++){
let objectToDealWith = snapshot.val().schools[returnCurrentUser()][studentName]['remarks'];
let remark = objectToDealWith[keys[i]]['remark'];
let examiner = objectToDealWith[keys[i]]['examiner'];
let fullRemark = ` ${examiner}: ${remark} | `
finalStuff += fullRemark;
}
});
return finalStuff;
}
I made it work by using this code.
ref.on("value", function(snapshot){
let dashTab = document.getElementById("dashboard_table");
let btn = document.getElementById("csv_blob");
let btn2 = document.getElementById("json_view");
let btn3 = document.getElementById("json_export");
btn.style.display = "block";
btn2.style.display = "block";
$("#json_export").css('display', 'block');
dashTab.innerHTML = "<thead><tr><th>Student Name</th><th>Class</th><th>Email</th><th>Subject</th><th>Project Info</th><th>Remarks</th><th>Project</th><th style='display: none;'>Project Download URL</th><th>Add Remark</th></tr></thead>";
let jsonRecieved = snapshot.val();
let objectToDealWith = snapshot.val().schools[returnCurrentUser()];
let lengthOfIt = Object.size(objectToDealWith)-1;
for(i=0;i<lengthOfIt;i++){
let finalRemark;
let int = i + 1;
let names = Object.keys(objectToDealWith);
let stdName = names[i];
let finalResult = objectToDealWith[stdName];
let stdClass = finalResult['class'];
let stdEmail = finalResult['email'];
let stdSubject = finalResult['subject'];
let stdiName = finalResult['stdname'];
for(var e=0;e<Object.size(objectToDealWith[stdName]['remarks']);e++){
let keys = Object.keys(objectToDealWith[stdName]['remarks']);
let remark = objectToDealWith[stdName]['remarks'][keys[e]]['remark'];
let examiner = objectToDealWith[stdName]['remarks'][keys[e]]['examiner'];
let completeRemark = ` | ${examiner} : ${remark} `
finalRemark += completeRemark;
}
let stdProjectName = finalResult['projectname']
let stdProjectInfo = finalResult['projectinfo'];
let stdProjectLink = finalResult['projectlink'];
let elementToPush = `<tr><td>${stdiName}</td><td>${stdClass}</td><td>${stdEmail}</td><td>${stdSubject}</td><td>${stdProjectInfo}</td><td>${finalRemark.replace(/undefined/g, '')}</td><td><a href=${stdProjectLink}>${stdProjectName}</a></td><td style='display:none;'>${stdProjectLink}</td><td id="${stdName}" style='text-align:center;' onclick="closeThatSomeThing();getIdOfTd(this.id)">➕</td></tr>`;
dashTab.innerHTML += elementToPush;
}
});
What i did here was that i turned i to e in the second loop and it worked...
I create this template successfully with javascript:
I create the template in an async function:
this.createBoxes = async function() {
var row_counter = 0;
for (var i = 1; i < this.fake_data.length + 1; i++) {
var item_box = document.createElement("div");
item_box.style.flex = "0.5";
item_box.style.backgroundColor = "white";
item_box.style.display = "flex";
item_box.style.flexDirection = "column";
item_box.style.justifyContent = "flex-end";
item_box.id = "item_box_"+i;
var item_name = document.createElement("h3");
item_name.style.flex = "0.2";
item_name.style.backgroundColor = "orange";
item_name.style.alignSelf = "center";
item_name.innerText = this.fake_data[i - 1].name;
item_name.id = "item_name_"+i;
item_box.appendChild(item_name);
this_row = document.getElementsByClassName("row")[row_counter];
this_row.appendChild(item_box);
if(i % 2 == 0) {
var pool = document.getElementById("pool");
var inner_row = document.createElement("div");
inner_row.style.display = "flex";
inner_row.style.flexDirection = "row";
inner_row.style.flex = "0.5";
inner_row.style.justifyContent = "space-around";
inner_row.style.alignItems = "center";
inner_row.style.backgroundColor = "green";
inner_row.className = "row";
pool.appendChild(inner_row);
row_counter++;
}
else if(i == this.fake_data.length) {
return;
}
}
}
Then I do this:
this.createBoxes().then(function() {
var template = document.querySelector('#pool');
var clone = template.content.cloneNode(true);
document.querySelector(".app").appendChild(clone);
})
But as you can see from my screenshot, .app is empty. What am I doing wrong? I am using Cordova and I am assuming that it is able to use the template tag, I haven't been able to find anything saying I can't.
UPDATE
This happens:
When I do this:
this.createBoxes().then(function() {
var template = document.querySelector('#pool');
var clone = template.cloneNode(true);
document.querySelector(".app").appendChild(clone);
});
Using template.cloneNode successfully moves the <template> but this is obviously not what I want, I want to get the contents of the <template> and move them to .app container, not the whole <template>.
You should be cloning the template's .content instead, as demonstrated in the documentation.
var temp = document.getElementsByTagName("template")[0];
var clon = temp.content.cloneNode(true);
document.body.appendChild(clon);
Well, if cloning the node itself works, then the answer is pretty simple - just clone/append children of the template:
this.createBoxes().then(function() {
let template = document.querySelector('#pool');
let app = document.querySelector(".app");
for(let child of template.childNodes) {
let clone = child.cloneNode(true);
app.appendChild(clone);
}
});
Note that I have not tested this code - you may need to debug it as necessary.
I added a container to the template programmatically:
var pool = document.getElementById("pool");
var container = document.createElement("div");
container.style.flex = "1";
container.style.backgroundColor = "white";
container.style.display = "flex";
container.style.flexDirection = "column";
container.id = "container";
var row = document.createElement("div");
row.style.display = "flex";
row.style.flexDirection = "row";
row.style.flex = "0.5";
row.style.justifyContent = "space-around";
row.style.alignItems = "center";
row.style.backgroundColor = "green";
row.className = "row";
container.appendChild(row);
pool.appendChild(container);
Then instead of adding my content to the #pool <template>, I added it to #container, and then stored the #container node in a variable, and then imported that into .app:
var container_in_temp = document.querySelector('#pool>#container');
var targetContainer = document.querySelector('.app');
targetContainer.appendChild(document.importNode(container_in_temp, true));
So it ends up looking like this, with a container in .app which is actually kind of preferable structure wise :).
I am new to object orientated programming in javascript and am trying to understand some functions in a project I am working on.
How would I call/run the internal function (the one listed 'this.getFieldset = function() {') to execute?
function Fieldset() {
this.id = "";
this.content = document.createElement("DIV");
this.content.id = "content";
this.title = "Title";
this.getFieldset = function() {
var div = document.createElement("DIV");
div.id = this.id;
var span = document.createElement("SPAN");
var fieldset = document.createElement("DIV");
fieldset.id = "fieldset";
var header = document.createElement("DIV");
header.id = "header";
span.appendChild(document.createTextNode(this.title));
header.appendChild(span);
div.appendChild(header);
div.appendChild(this.content);
div.appendChild(fieldset);
return div;
}
}
var myFieldset = new Fieldset();
myFieldset.getFieldset();
First you should create an instance of Fieldset, then you'll be able to call its functions (called methods):
var myFieldset = new Fieldset();
myFieldset.getFieldset();
function Fieldset() {
this.id = "";
this.content = document.createElement("DIV");
this.content.id = "content";
this.title = "Title";
this.getFieldset = function() {
var div = document.createElement("DIV");
div.id = this.id;
var span = document.createElement("SPAN");
//var fieldset = document.createElement("DIV");
//fieldset.id = "fieldset";
var header = document.createElement("DIV");
header.id = "header";
span.appendChild(document.createTextNode(this.title));
header.appendChild(span);
div.appendChild(header);
div.appendChild(this.content);
div.appendChild(fieldset);
window.alert("test");
return div;
}
//add call to run function
this.getFieldset();
}