Problem with infinite loop when manipulating DOM - javascript

I'm learning about DOM manipulation and, to practice, I'm trying to get the first 100 Twitter users who have twitted about #Javascript (see link). As, for now, Twitter doesn't allow you to use console.log() function in the browser console, I have managed to show any string visually in the HTML, in this case, under the search textbox.
This is my "custom" console.log()
function consoleLog(data) {
var searchTextBox = document.querySelector("#doc > div.topbar.js-topbar > div > div > div > div > div");
var p = document.createElement("p");
var innerText = document.createTextNode(data);
p.appendChild(innerText);
searchTextBox.appendChild(p);
}
For getting the usernames, I keep scrolling the page every 4 seconds and looking for usernames until I have 100 or more of them in my usernames variable.
var scrollPage = setInterval(function() {
window.scrollTo(0, document.body.scrollHeight);
}, 4000);
var usernames = [];
while (true) { // <------ PROBLEM
if (usernames.length < 100) {
consoleLog("Getting usernames again");
usernames = getUsernames();
}
else {
consoleLog("We have ENOUGH usernames. BREAK");
clearInterval(scrollPage);
printUsernames();
break;
}
}
function printUsernames() {
for(var user of usernames) {
consoleLog(user);
}
}
function getUsernames() {
var results = [];
var usernameNodes = document.getElementsByClassName("username u-dir u-textTruncate");
var username = usernameNodes[0].textContent;
for(var node of usernameNodes) {
results.push(node.textContent);
}
return results.filter(isUnique);
}
function isUnique(value, index, self) {
return self.indexOf(value) === index;
}
The problem is that the while loop enters in infinte loop and I don't know why. I think the logic of the code is correct. In fact, if I first copy and paste all the declared functions to the browser console, then start the scrollPage interval and, lastly, start the while loop, it works well. The problem comes when I copy and paste all the code at one time in the browser console. It is like the executions of the interval and the while loop conflict in some way. But I can't understand.

Its better to have while conditioned like this:
var usernames = [];
// This will automatically end when length is greater or equall 100
// no need to break
while (usernames.length < 100) {
consoleLog("Getting usernames again");
usernames = getUsernames();
}
consoleLog("We have ENOUGH usernames.");
clearInterval(scrollPage);
printUsernames();

Related

How do I use data out of chrome.storage.get function?

I'm trying to grab data from chrome extension storage, but I can use them only in this function.
var help = new Array();
chrome.storage.local.get(null, function(storage){
//get data from extension storage
help = storage;
console.log(storage);
});
console.log(help); // empty
Result in console:
content.js:1 content script running
content.js:11 []
content.js:8 {/in/%E5%BF%97%E9%B9%8F-%E6%99%8F-013799151/: "link", /in/adam-
isaacs-690506ab/: "link", /in/alex-campbell-brown-832a09a0/: "link",
/in/alex-davies-41513a90/: "link", /in/alex-dunne-688a71a8/: "link", …}
Async function has won. I wrote my code again and now function is called hundreds time, i can not do this in dirrefent way
code:
console.log("content script running");
var cards = document.getElementsByClassName("org-alumni-profile-card");
var searchText = "Connect";
function check(exi, cards) {
chrome.storage.local.get(null, function(storage) {
for (var key in storage) {
if (storage[key] == "link" && key == exi) {
cards.style.opacity = "0.3";
}
}
});
}
for (var i = 0; i < cards.length; i++) {
var ctd = cards[i].getElementsByClassName(
"org-alumni-profile-card__link-text"
);
var msg = cards[i].getElementsByClassName(
"org-alumni-profile-card__messaging-button-shrunk"
);
if (ctd.length < 1 || msg.length > 0) {
cards[i].style.display = "none";
} else {
var exi = cards[i]
.getElementsByClassName("org-alumni-profile-card__full-name-link")[0]
.getAttribute("href");
check(exi, cards[i]);
}
}
SOLUTION of my problem
I wanted to delete this topic, but I can not, so instead of doing that, I'll put here what I've done finally.
The code above is wrong becouse, it was taking a list of links from website and for each from them script was grabbing a data from a storage... Which was stupid of course. I didn't see a solution which was so easy:
Put all your file's code in this function - it grabs data from storage just once.
I'm so sorry for messing up this wonderfull forum with topic like this.
Hope u'll forgive.
help will return undefined because it is referencing a asynchronous function and not the return value of that function. The content from storage looks to be printed on content.js:8, i.e. line 8.

how to search and compare the items in the array gotten from the output of a loop after the loop is complete (javascript)

please im having a bit of a problem with my javascript code.... im working on a reminder app in Cordova and im using Katzer notification plugin... now i am coding in javascript and im having a little challenge. im trying to achieve a feature whereby , when a user tries to add a reminder that already exists, it will throw an error... and if the reminder dosen't exist, it will add it to the list of reminders.... im using as javascript loop for this... heres my code
function checkifReminderExists(){
cordova.plugins.notification.local.getAll(function (notifications) {
var allRemInfo = "";
var newAllRemInfo = "";
// using while loop
var count = 0;
while (count < notifications.length) {
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text ;
if(allRemInfo.indexOf(""+checkedBoxes+"") == true)
{
alert("sorry you cant add a reminder that already exist...");
} else {
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}
});
count++
continue;
}
});
}
/* the above did not work so i tried using for loop to achieve this
function checkifReminderExists(){
cordova.plugins.notification.local.getAll(function (notifications) {
var allRemInfo = "";
var newAllRemInfo = "";
var count;
for(count = 0; count < notifications.length; count++)
{
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text + ", " ;
newAllRemInfo = new Array(""+allRemInfo+"");
if(newAllRemInfo.indexOf(""+checkedBoxes+"") == true)
{
alert("sorry you cant add a reminder that already exist...");
} else
{
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}
});
}
});
}
I tried both methods(for and while loop) above and none of them gave me my result... instead the "if()else" test will run separately on each of the loop...the disadvantage of this, is that when a test runs on the first item in the list of reminders, the setLecReminders(); function runs irrespective of if the subsequent test are true or false... i want a solution whereby the loop runs completely first and all items on the list are outputted into an array and then i can use a if()else test on all members of the array simultaneously. Please pardon my long question... thanks in advance
I would suggest setting a boolean inside of your loop like this:
var reminderExists = false;
while (count < notifications.length)
{
cordova.plugins.notification.local.get(count, function (notification) {
allRemInfo = allRemInfo + notification.text;
if (allRemInfo.indexOf(""+checkedBoxes+"") == true)
{
reminderExists = true;
}
count++;
}
Then you can check the boolean outside of the loop and show your alerts based on that result:
if (reminderExists) {
alert("sorry you cant add a reminder that already exist...");
} else {
alert("there is no similarity so im going ahead to create the reminder now...");
setLecReminders();
}

A pattern to queue notifications?

I created a library to pop up some toast notifications and I tried to put a limit on the maximum notifications on screen.
I managed to extract the idea into a plunker (don't mind the code, it is only to solve the issue).
I have a function to create those toasts:
function createToast() {
var body = $document.find('body').eq(0);
var toast = {};
toast.id = index++;
toast.el = angular.element('<div class="toast">Toast ' + toast.id + '</div>');
toast.el = $compile(toast.el)($scope);
if (maxOpened && toasts.length >= maxOpened) {
remove(toasts[0].id);
}
toasts.push(toast);
$animate.enter(toast.el, body).then(function() {
$timeout(function() {
remove(toast.id);
}, 3000);
});
}
Basically it creates a new object with an el and then animates it out on the body. Notice that if the maxOpened is reached it removes the first one.
function remove(id) {
var toast = findToast(id);
if (toast) {
$animate.leave(toast.el).then(function() {
var index = toasts.indexOf(toast);
toasts.splice(index, 1);
});
}
function findToast(toastId) {
for (var i = 0; i < toasts.length; i++) {
if (toasts[i].id === id) {
return toasts[i];
}
}
}
}
Find the toast, animate the leave and then delete it.
If I do a $interval on them, let's say 600ms it works.
Try here: http://plnkr.co/edit/lDnT57FPadCt5Ir5wHuK?p=preview
If you lower it to something like 100ms it starts to break, not only ignoring the max but also leaving some orphan toasts that won't get deleted.
So I am not sure what could be a good solution here. My best guess is to provide a queue so I start to drain it as soon as a toast get removed but so far, I didn't make it.
The probably simplest solution would be to add a deferred to each toast and only start to animate the toast when the limit is not or no longer reached.
You start by adding a deferred and resolve it immediately, if the limit is not reached yet or the limit can be ignored:
toast.slotOpen = $q.defer();
toasts.push(toast);
if (maxOpened && toasts.length <= maxOpened || !maxOpened) { // i guess 0 or a falsy value means to ignore the limit
toast.slotOpen.resolve();
}
You only start the animation, when a slot is open:
toast.slotOpen.promise.then(function() {
$animate.enter(toast.el, body).then(function() {
The last thing to do is to resolve the deferred when a new slot gets opened after an old toast has been removed:
$animate.leave(toast.el).then(function() {
var index = toasts.indexOf(toast);
toasts.splice(index, 1);
if (maxOpened && toasts.length >= maxOpened) {
toasts[maxOpened - 1].slotOpen.resolve();
}
I have adjusted your code and created a new Plunker.

Underscore.js performance issue - Can i use _.defer

In an simple web app that i am building using underscore.js and jquery. For a list of all people ( js object ) I am filtering out list of all the places (js object) they visited. People list is a html table with a td having places image icon which on click displays list of all places they visited. Icon needs to be shown only for people who have visited at the least one place. The problem here is that people and places count comes around 2000, 100. So the code below executes 2000*100 combinations. The browser complains me of unresponsive script. Code is provided below
_.each(peopleList, function (person, index, list) {
//filter the respective places for people
var visitedPlaces = _.filter(places, function (place) {
return place.PeopleId == person.Id;
});
if (_.isEmpty(visitedPlaces)) {
$("a#" + place.PeopleId).remove();
}
});
Dead simple isn't it. For each person check if visited places has him tracked. How do i optimize the above code to unblocking and responsive. Tried putting in _.defer and _.delay at some places but no improvement
FWIW, here is how I would solve it in underscore.
function removeNonTravelers(people, visits) {
var travelers = _.pluck(visits, 'PeopleId'),
nonTravelers = _.reject(people, function (person) {
return _.contains(travelers, person.Id);
});
$(_.map(nonTravelers, document.getElementById)).remove();
}
http://jsfiddle.net/FWzeN/
My suggestion would be to drop underscore and use plain JS for this:
function removeNonTravelers(people, visits) {
var i, peopleId,
numPeople = people.length,
numVisits = visits.length,
index = {}, nonTravelers = [];
// index
for (i = 0; i < numVisits; i++) {
peopleId = visits[i].PeopleId;
if (!index.hasOwnProperty(peopleId)) {
index[peopleId] = 1;
} else {
index[peopleId]++;
}
}
// find HTML elements to remove
for (i = 0; i < numPeople; i++) {
peopleId = people[i].Id;
if (!index.hasOwnProperty(peopleId)) {
nonTravelers.push(document.getElementById(peopleId));
}
}
// remove them all at once
    $(nonTravelers).remove();
}
This is reasonably fast. If I didn't make any mistake, your test case (2000 people, 100 places) times at more than 700 operations per second on my rather outdated laptop (DOM operations excluded).
Try for yourself: http://jsperf.com/where-not-exists-in-javascript
var hashMap = {};
_.each(places, function(place) {
hashMap[place.PeopleId] = place;
});
_.each(peopleList, function (person, index, list) {
//filter the respective project documents
var visitedPlaces = hashMap[person.id];
if (visitedPlaces) {
$("a#" + place.PeopleId).remove();
}
});

Executing Javascript code after loading refreshed page

I have a Javascript file that is split into to two parts. The first part of the code ends by refreshing the current page. I would like to try to get the second part of the code to execute as soon as the page reloads, but I am having trouble finding a way to implement this. I pretty much want a way to do
window.onload = someFunction()
except that it activates the function after reloading the page due to the first part of the Javascript. Is there an effective way to do this?
You could do that using Jquery.
This is executed on page loading
$(function() {
callMyFunction()
});
Use
document.onload = somefunction()
Instead . This will get executed immediately after the DOM loads .
You can also use the jQuery to do the same like
$(document).ready(function(){
somefunction();
});
The only way I can think of is to add query string value when refreshing, then read that value and act upon it.
You can use such code:
function ParseQueryString() {
var result = [];
var strQS = window.location.href;
var index = strQS.indexOf("?");
if (index > 0) {
var temp = strQS.split("?");
var arrData = temp[1].split("&");
for (var i = 0; i < arrData.length; i++) {
temp = arrData[i].split("=");
var key = temp[0];
var value = temp.length > 0 ? temp[1] : "";
result[key] = value;
}
}
return result;
}
window.onload = function WindowLoad() {
var QS = ParseQueryString();
var reloaded = QS["reloaded"];
if (reloaded === "1") {
//execute second part of code
}
}
Then when reloading, redirect to same page adding ?reloaded=1 otherwise (if this flag is already raised) don't refresh the page again to avoid infinite loop.

Categories

Resources