I'm trying to build a simple tool that will return a Github profile when you search someone's username. Everything seems to be working, except when I search for a different user, the list of followers from the previous user search don't clear.
For example, a user who has seven followers will suddenly have dozens of follower avatars displaying.
Can anyone tell me how to display the correct number of followers unique to each user when fetching different Github profiles?
var response = null;
var followers = null;
document.getElementsByTagName('button')[0].addEventListener('click', function(r) {
getUser(document.getElementsByTagName('input')[0].value);
});
function getUser(name) {
fetch('https://api.github.com/users/' + name)
.then(function(r) {
console.log(r.status);
return r.json();
})
.then(function(j) {
response = j;
assignValues();
getFollowers(j.followers_url);
});
}
function assignValues() {
document.getElementById('loader').style = 'display: none';
document.getElementById('avatar').src = response.avatar_url;
document.getElementById('name').innerText = response.name;
document.getElementById('username').innerText = response.login;
document.getElementById('location').innerText = response.location;
document.getElementById('bio').innerText = response.bio;
document.getElementById('count').innerText = 'Followers: ' + response.followers;
}
function getFollowers(url) {
fetch(url)
.then(function(r) {
return r.json();
})
.then(function(f) {
followers = f;
listFollowers();
});
}
function listFollowers() {
followers.forEach(function(f) {
var li = document.createElement('li');
li.innerHTML = ''+ '<img src="' + f.avatar_url + '" alt="' + f.login + '"/>'+ '';
document.getElementById('list').appendChild(li);
});
}
You need to clear that #list element in listFollowers function before starting appending new followers to it. For example:
var list = document.getElementById('list');
list.innerHTML = '';
followers.forEach(function(f) {
var li = document.createElement('li');
li.innerHTML = ''+ '<img src="' + f.avatar_url + '" alt="' + f.login + '"/>'+ '';
list.appendChild(li);
});
Three sidenotes here:
why do you use global variables response and followers in your rendering functions when you can pass those as arguments?
as you rerender a 'user profile' first, then wait for avatars' fetch, it's still possible for a user to see the followers of user X when looking for user Y. It's better to clear that section (and show some visual cue for loading data) immediately after the user is switched.
as order of fetch responses is not guaranteed, there's a potential race condition here: if user clicks on that button twice (with different inputs), the earlier input might get back later - and overwrite the former. You need to guard against this, either by storing the latest input value and checking the response, or by some other means.
You can test those by artificially throttling the network speed. And believe me, in real world most of your user will have all sorts of problems with that.
Related
I currently have a page that has content that expands when you click on a term, but as soon as you click on a new term the old one closes and the new one expands. The terms are loaded in from a google sheet onto the page. This is on a HTML page but the javascript code to do the work is the following:
// Address of the Google Sheets Database
var public_spreadsheet_url = 'sheet link here';
// Column Names from Google Sheets Database
let questionsColumn = "Question";
let answersColumn = "Answer";
window.addEventListener('DOMContentLoaded', init) // Calls method init when Sheets has loaded
function init() {
Tabletop.init( { key: public_spreadsheet_url,
callback: showInfo,
simpleSheet: true } );
}
var unhiddenAnswer = "";
// Method that gets called when data has been pulled from Google Sheets
function showInfo(data) {
var editButton = '<center><a style="border-bottom: none" href="' + public_spreadsheet_url + '"><button class="button admin">Edit</button></a></center>';
// Injects the built HTML code into the div Dynamic
document.getElementById("dynamic").innerHTML = buildFAQTable(data) + editButton;
}
// Builds the HTML Table code from the Database Data
function buildFAQTable(data) {
var index = 0;
var content = '<h2>Title Here</h2><div style="padding:0px 5%">';
data.forEach(form => {
content += '<h1 class="faq_question" onClick="unhideAnswer(' + index + ')">' + data[index][questionsColumn] + '</h1>';
content += '<p id="answer' + index + '" class="hideAnswer">' + data[index][answersColumn] + '</p>';
index++;
});
// Extends body to accomdate for tall footer on very small devices (e.g. iPhone 5/5S/SE)
content += "<br></br><br></br>";
return content;
}
// When a FAQ Question gets clicked on, this method will hide the currently displaying answer (if any), and
// Unhide the answer corresponding to the clicked on answer.
// If the currently displaying answer is the same as the answer corresponding to the clicked on question,
// it will be hidden and no new answer will be unhidden
function unhideAnswer(number) {
var answerID = "answer" + number;
if (answerID != unhiddenAnswer) {
document.getElementById(answerID).classList.remove("hideAnswer");
}
if (unhiddenAnswer != "")
document.getElementById(unhiddenAnswer).classList.add("hideAnswer");
if (unhiddenAnswer == answerID)
unhiddenAnswer = ""
else
unhiddenAnswer = answerID;
}
I want to now add an expand all/ collapse all button to give the user the option to open and view all the terms at one if needed. However, if not using the expand all button, the regular open and close functionality above should be used. I am new to javascript and am at a loss on the best way to implement this. Any help would be appreciated.
add a answer class to every answer, then you can loop through all of them with this query selector
// in your buildFAQTable fucntion
content += '<p id="answer' + index + '" class="hideAnswer answer">' + data[index][answersColumn] + '</p>';
document.querySelectorAll('.answer').forEach(answer => {
// you can use toggle, add or remove to change the appearance of the answer
answer.classList.toggle('hideAnswer')
})
i would also recomend you to check out some of the newer javascript features like string interpolation and avoid using var, but it is not so important if you are just starting out.
(i also refactored some of your code, this might make it a bit more readable)
// Address of the Google Sheets Database
const public_spreadsheet_url = 'sheet link here';
// Column Names from Google Sheets Database
const questionsColumn = "Question";
const answersColumn = "Answer";
function toggleAnswer(num) {
const answer = document.getElementById(`answer${num}`);
answer.classList.toggle('hideAnswer');
}
function hideAll() {
document.querySelectorAll('answer').forEach(answer => {
answer.classList.add('hideAnswer');
})
}
function showAll() {
document.querySelectorAll('answer').forEach(answer => {
answer.classList.remove('hideAnswer');
})
}
function buildFAQTable(data) {
let index = 0;
let content = '<h2>Title Here</h2><div style="padding:0px 5%">';
for (i in data) {
content += `<h1 class="faq_question" onClick="unhideAnswer(${i})">${data[i][questionsColumn]}</h1>`;
content += `<p id="answer${i}" class="hideAnswer answer">${data[i][answersColumn]}</p>`;
}
content += "<br></br><br></br>";
return content;
}
function showInfo(data) {
const editButton = `<center><a style="border-bottom: none" href="${public_spreadsheet_url}"><button class="button admin">Edit</button></a></center>`;
document.getElementById("dynamic").innerHTML = buildFAQTable(data) + editButton;
}
window.addEventListener('DOMContentLoaded', () => {
Tabletop.init({
key: public_spreadsheet_url,
callback: showInfo,
simpleSheet: true
});
}, { once: true })
I'm making a food sharing site using firebase I'm trying to add a delete post option so when a food item is taken the user can delete the post so they aren't contacted about something they've already given away. I have managed to get the firebase remove function working but it just deletes all posts not just one. I'm using the push option when sending the data to my database as I need users to be able to make multiple posts but I don't know what the name of the post ids are to select them.
This is the code that sends the donations to the database
function submitDonation() {
var user = firebase.auth().currentUser;
if(user) {
var userid = user.uid;
var email = user.email;
var firebaseRef = firebase.database().ref();
firebaseRef.child("Donations").push({
user: userid,
email: email,
food: document.getElementById("foodType").value,
animal: document.getElementById("animalType").value,
expire: document.getElementById("expirationDate").value,
travel: document.getElementById("travelDistance").value,
location: document.getElementById("what3wordsLocation").value,
contact: document.getElementById("contactPreference").value,
time: document.getElementById("timePreference").value
});
}
}
and this is the code retrieving and drawing the data on the page. I'm using jquery to display my posts so sorry it looks a bit messy but I've basically got a button that calls a function and I need the button's id to match the id of the post but I don't know how to call that. Any help would be much appreciated. I'm still relatively new to firebase so still working on the basics aha.
var rootRef = firebase.database().ref().child("Donations");
rootRef.on("child_added", snap => {
var food = snap.child("food").val();
var animal = snap.child("animal").val();
var expire = snap.child("expire").val();
var travel = snap.child("travel").val();
var location = snap.child("location").val();
var contact = snap.child("contact").val();
var time = snap.child("time").val();
$("#listingDonations").append(
"<div id='donationBox' class='three columns'><div id='donationItem'><button onclick='deletePost()'>Delete</button><p><b>Type of Food:</b> " + food + "</p><p><b>Type of Animal:</b> " + animal + "</p><p><b>Expiration Date:</b> " + expire + "</p><p><b>Travel Distance:</b> " + travel + "</p><p><b>Contact Details:</b> " + contact + "</p><p><b>Preferred Contact Time:</b> " + time + "</p><a href='https://www.what3words.com/"+ location +"'>Location</a></div></div>"
);
})
function deletePost() {
}
Ive deleted the code I'd written in the deletePost function as it didn't work and is probably wrong anyway.
I have looked at all of the questions around windows.locaton.href and windows.locaton.replace not working, but still can't figure out why this redirect is not working in JavaScript. There are two JS functions I am calling when a button is clicked with submit.
<input type="submit"
onclick="NotifyUserOfNewBudgets('#Field1');redirect2MainLookup('#primaryFilename');"
class="SaveChangeButton" value="Create New Budget">
The two functions are defined in Javascript as:
<script>
function NotifyUserOfNewBudgets(val) {
alert("New Budget will be saved. NewVal=" + val);
var ireturn;
document.getElementById("NewBudgetID").value = val;
document.getElementById("formMode").value = "Update";
}
function redirect2MainLookup(primaryFilename) {
var loc = window.location.pathname;
var host = document.location.host;
var dir = loc.substring(0, loc.lastIndexOf('/'));
//Replace the word Edit with blank so this redirects correctly
var newdir = dir.replace("NewBudget", "");
var newpath = host + newdir + primaryFilename;
alert('newpath location = http://' + newpath);
try {
windows.locaton.href = "http://" + newpath;
//window.location.replace('http://' + newpath);
} catch (err) { alert("Error: " + err);}
}
</script>
The error I get in the try()catch() is windows is not defined and then is stays on the same page. I get the same error using windows.locaton.replace() too. I have lots of pages doing redirects, can't figure out why this one fails.
You have a number of spelling mistakes. window is the object you are looking to reference. location is the property you are looking to access. Right now, you are using windows.locaton. windows is not a thing, nor is locaton. Keep an eye on undefined errors, they can tell you a lot about the state of your code.
I have been trying to debug for this days now, and I’m not sure what the problem is.
To give you a little background:
I'm working on a project that pulls the top headline on the politics section of a far left news source (Huffington Post), a moderate left (CNN), a moderate right (Fox), and a far right (Breitbart).
From here, I’m finding Reddit posts referencing that article and appending it to the html. All of this is being done through YQL.
Here's an example with how I'm using CNN.
//CNN
$(function getCNNNews() {
var statement = "select * from feed where url='http://rss.cnn.com/rss/cnn_allpolitics.rss'";
$.queryYQL(statement, "json", undefined, function (data) {
// do something with "data".
console.log(data.query.results.item);
//Get first object in array
console.log(data.query.results.item[0]);
var firstObjectCNN = data.query.results.item[0];
$("#col2").append("<h1 id='cnn'>" + firstObjectCNN.title + "</h1>");
$("#col2").append("<h4 id='cnn'> Published by CNN <br/>" + firstObjectCNN.pubDate + "</h4>");
//Search for the top post referencing that headline on Reddit
$(function getCNNPostReddit() {
var newStatement = encodeURIComponent(firstObjectCNN.title).replace(/'/g , "%27");
var statement = "select * from feed where url='https://www.reddit.com/search.xml?q=" + newStatement + "&sort=new'";
$.queryYQL(statement, "json", undefined, function (data) {
console.log(statement);
console.log(data);
var firstCNNEntryResults = data.query.results;
if (firstCNNEntryResults == null)
{
document.getElementById("loading2").style.display = 'inline-block';
}
else
{
// Get first entry's (which is the entry with the most comments) rss feed containing comments
var firstCNNEntry = data.query.results.entry[0];
console.log("select * from feed where url='" + firstCNNEntry.link.href + ".rss");
// Erase end of URL that's not needed
var firstCNNEntryLink = firstCNNEntry.link.href;
firstCNNEntryLink = firstCNNEntryLink.substring(0, firstCNNEntryLink.indexOf('?'));
console.log(firstCNNEntryLink);
//Create a dynamic rss feed based on link to first entry; this is where the comments will come from.
$(function getCNNRedditComments() {
var statement = "select * from feed where url='" + firstCNNEntryLink + ".rss'" ;
$.queryYQL(statement, "json", undefined, function (data) {
console.log(data.query.results.entry);
//Start with the 4th comment; since the first 3 comments are auto moderator
for (var i = 0; i < data.query.results.entry.length; i++) {
console.log(data.query.results.entry[i].content.content);
$("#col2 #comment-box").append("<div id='comment'><span id='username'>" + data.query.results.entry[i].author.name + "</span>" + ":" + data.query.results.entry[i].content.content + "</div>")
}
});
});
}
});
});
});
});
I've made it so when the results come out null, I replace the comments with a loading symbol. The issue I'm having is, sometimes the comments will show, and other times they won't.
The current state of the site is here:
leftright.info
Reddit has a limit to how often you can fetch their RSS feeds. I had that problem too, so I came up with a workaround. I've created a public tool for it on my website.
https://burkybang.com/reddit_rss
this is my first time here as a poster, please be gentle! I have zero knowledge of JS (yet, working on it) but am required to do some JS anyway. Here's my problem. I got some code (not mine) allowing a user to select multiple choices. I found the function that gathers these choices and store them
function getProductAttribute()
{
// get product attribute id
product_attribute_id = $('#idCombination').val();
product_id = $('#product_page_product_id').val();
// get every attributes values
request = '';
//create a temporary 'tab_attributes' array containing the choices of the customer
var tab_attributes = [];
$('#attributes select, #attributes input[type=hidden], #attributes input[type=radio]:checked').each(function(){
tab_attributes.push($(this).val());
});
// build new request
for (var i in attributesCombinations)
for (var a in tab_attributes)
if (attributesCombinations[i]['id_attribute'] === tab_attributes[a])
request += '/'+attributesCombinations[i]['group'] + '-' + attributesCombinations[i]['attribute'];
$('#[attsummary]').html($('#[attsummary]').html() + attributesCombinations[i]['group']+': '+attributesCombinations[i]['attribute']+'<br/>')// DISPLAY ATTRIBUTES SUMMARY
request = request.replace(request.substring(0, 1), '#/');
url = window.location + '';
// redirection
if (url.indexOf('#') != -1)
url = url.substring(0, url.indexOf('#'));
// set ipa to the customization form
$('#customizationForm').attr('action', $('#customizationForm').attr('action') + request);
window.location = url + request;
}
I need to make a simple display summary of these choices. After quite a bit of searching and findling, I came with the line with the DISPLAY SUMMARY comment, this one:
$('#[attsummary]').html($('#[attsummary]').html() + attributesCombinations[i]['group']+': '+attributesCombinations[i]['attribute']+'<br/>')
In the page where I want those options, I added an empty div with the same ID (attsummary):
<div id="attsummary"></div>
Obviously, it is not working. I know I don't know JS, but naively I really thought this would do the trick. May you share with me some pointers as to where I went wrong?
Thank you very much.
Correct form of the line it isn't working for you:
$('#attsummary').html($('#attsummary').html() + attributesCombinations[i]['group']+': '+attributesCombinations[i]['attribute']+'<br/>')