see changes in DOM elements via JavaScript in my Firefox Extension - javascript

i'm writing a Firefox Extension. i need listen to changes in some DOM objects and the log them.
a can't find something specific in JavaScript like DOM changing listener.
a have trouble with infinite loop.
function get_values(){
var names = document.getElementsByClassName('X');
var new_names = [];
for (i=0; i<names.length; i++){
if(names[i].innerText==""){
continue
}
new_names.push(names[i].innerText);
}
return new_names
}
function check_values(names){
new_names = get_values()
if (new_names == names){
return 0
}else{
return new_names
}
}
do = true
var names = []
while(do){
new_values = check_values(names)
if ( new_values != 0){
names = new_values
console.log('___________log_____________')
console.log(names)
}
}
without while expression code is running,but with this code i want listen to changes an log it in console.
with while expression i can't see any log in console.
i new to JavaScript(10H)

Related

javascript] Object's value update strangely

The value of the object is updated very strangely.
the current overall system structure is as follows.
There is a server that collects the status of each system.
Send the collected data from the server to the web server through websocket
When the web server receives the websocket, the callback function is called.
In the callback function, the object is updated with the received data.
The problem occurs when updating objects.
Here is the code for that part.
var systemDatas = {};
...
fn_callback = function(data){
fn_set_metric(data);
...
};
...
function fn_set_metric(data){
Object.entries(data).forEach(([apps, appArr]) => {
for(let i = 0; i < appArr.length; i++){
var app = {};
if(appArr[i].name === "GW"){
if(systemDatas.hasOwnProperty("GW")){
var gwDatas = systemDatas["GW"];
Object.keys(gwDatas).map(function(key){
try {
var keyIdx = 0;
for(let j = 0; j < (appArr[i].nodes).length ; j++){
if(appArr[i].nodes[j].name === key){
keyIdx = j;
break;
}
}
if(appArr[i].nodes[keyIdx].health === "on"){
gwDatas[key].process.cpuSystem = appArr[i].nodes[keyIdx].metrics[0].measurements[0].value;
gwDatas[key].process.cpuProcess = appArr[i].nodes[keyIdx].metrics[1].measurements[0].value;
gwDatas[key].memory.memUsed = appArr[i].nodes[keyIdx].metrics[2].measurements[0].value;
gwDatas[key].memory.heapUsed = appArr[i].nodes[keyIdx].metrics[4].measurements[0].value;
gwDatas[key].thread.threadDeamon = appArr[i].nodes[keyIdx].metrics[6].measurements[0].value;
gwDatas[key].thread.threadLive = appArr[i].nodes[keyIdx].metrics[7].measurements[0].value;
gwDatas[key].memory.memMax = appArr[i].nodes[keyIdx].metrics[3].measurements[0].value;
gwDatas[key].memory.heapMax = appArr[i].nodes[keyIdx].metrics[5].measurements[0].value;
gwDatas[key].thread.threadPeak = appArr[i].nodes[keyIdx].metrics[8].measurements[0].value;
gwDatas[key].process.uptime = appArr[i].nodes[keyIdx].metrics[9].measurements[0].value;
gwDatas[key].process.cpuCount = appArr[i].nodes[keyIdx].metrics[10].measurements[0].value;
console.log(key);
console.log(systemDatas["GW"][key].process.uptime);
console.log(systemDatas["GW"][key].process);
console.log(systemDatas["GW"][key]);
console.log(systemDatas["GW"]);
}
}
catch(e) {
console.error(e);
}
});
}
...
}
and the result of executing the function.
console.log
As you can see in the area marked in yellow in the result image. depending on the scope of the object, the value is different.
my expectation is
after systemDatas["GW"]["GW_1"] is updated, systemDatas["GW"]["GW_2"] is updated. sequentially.
but it's behaving in an incomprehensible way
except the callback function there is no part to update systemDatas.
Can you explain why it works this way?
Your code complexity (nesting) is to high - It is not helping you solve the problem.
Fixes
Break the function up into 2-3 separate functions const parseMetricsData, parseGWData; // etc..
Look over latest added Array methods, some of the new ones like [].find will make the code easier to read (MDN Array Docs).
Other tips after code example.
Example:
const systemDatas = {};
// ...
const fn_callback = function (data) {
fn_set_metric(data);
// ...
};
// ...
const parseGWData = (app, gwDatas) => {
for (const key of gwDatas.keys()) {
const gwData = gwData || {},
foundNode = !app.nodes ? null : app.nodes.find(n => n.name === key);
if (!foundNode || foundNode.health !== 'on') continue;
gwData.process.cpuSystem = foundNode.metrics[0].measurements[0].value;
gwData.process.cpuProcess = foundNode.metrics[1].measurements[0].value;
gwData.process.uptime = foundNode.metrics[9].measurements[0].value;
gwData.process.cpuCount = foundNode.metrics[10].measurements[0].value;
gwData.memory.memUsed = foundNode.metrics[2].measurements[0].value;
gwData.memory.heapUsed = foundNode.metrics[4].measurements[0].value;
gwData.memory.memMax = foundNode.metrics[3].measurements[0].value;
gwData.memory.heapMax = foundNode.metrics[5].measurements[0].value;
gwData.thread.threadDeamon = foundNode.metrics[6].measurements[0].value;
gwData.thread.threadLive = foundNode.metrics[7].measurements[0].value;
gwData.thread.threadPeak = foundNode.metrics[8].measurements[0].value;
console.log(key);
console.table(systemDatas.GW[key])
}
};
function fn_set_metric(data) {
for (const [apps, appArr] of Object.entries(data)) {
for (const app of appArr) {
if (app.name !== 'GW' ||
!Object.prototype.hasOwnProperty.call(systemDatas, 'GW')) continue;
parseGWData(systemDatas.GW);
}
}
}
Other code tips:
Put long property chains into variables, either via built-ins (app.nodes.find(app => app.name === key)) or directly.
Use built-ins (Array.prototype.find, for of loops etc. (use whatever your platform/platform version supports (see MDN Array, etc., for more).
Use negative if checks (instead of nesting main part of code in if statements you can check the opposite condition to avoid creating deeply nested code).
~~Consider not mutating static structures until loops/manipulations are complete; E.g., perform manipulations on pure, new, objects and then merge results into static structure(s) - will help you pinpoint issues~~ Consider that appArr may have duplicate app entries which may be overriding each others' values.

Problem with infinite loop when manipulating DOM

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();

execCommand('copy') not working programatically, only when executed via DevTools console

Source:
const package = document.querySelector('td[data-bind="text: packageName"');
if (package.textContent.indexOf('Adaptive') !== -1) {
package.click();
const stacks_tab = document.querySelector('ul[class="tabsExpanded"]').children[5];
stacks_tab.click();
function get_sources() {
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
if (copy === '') {
setTimeout(get_sources, 500);
} else {
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
function copy_sources() {
console.log('running');
hidden.select();
if (!document.execCommand('copy')) {
setTimeout(copy_sources, 500);
} else {
console.log('Sources copied!');
}
}
copy_sources();
}
}
get_sources();
} else {
console.log('There is no Adaptive package in this content.');
}
Line 45 is what isn't working.
That code won't make a lot of sense, but here's the use case:
I'm trying to automate part of my job by injecting some JavaScript into the Chrome DevTools console on our CMS that we use for video content where I work. What the script does is click a few elements, then grabs some file locations and copies them to the clipboard as comma separated values. I had this working just fine before, but I decided to try and make the script better...and now the document.execCommand('copy') is just not working.
As you can see, I use some recursion to continuously select the hidden input value and then I try to copy it, and if it fails, I try again in 500 ms. I also log 'running' to ensure the function is actually running (it is). The execCommand() function keeps returning false every 500ms. BUT, if I type it into the console manually and run it, it returns true and works fine even as the recursive function continues to return false. So for some reason, it won't work in the context of my script, but works totally fine when run manually.
Like I said before, it WAS working programatically before, but I changed some stuff to make the script better and more automated, and it won't work anymore. Here's the code with execCommand() working fine:
const sources = [];
const stacks = document.querySelectorAll('span[data-bind="text:duration"]');
for (let i = 0; i < stacks.length; i++) {
stacks[i].click();
let renditions = document.querySelectorAll('span[class="blockUnSelected"]');
renditions[(i+1) * 8 - 1].click();
sources.push(document.querySelectorAll('p[data-bind="text: $data.name"]')[0].textContent);
}
let copy = '';
for (let i = 0; i < sources.length; i++) {
const change_brackets = sources[i].replace(/\/tveorigin\/vod\/ae\//, '');
const no_pd1 = change_brackets.replace(/-pd1/g, '');
copy += no_pd1 + ',';
}
const hidden = document.createElement('input');
hidden.value = copy;
document.querySelector('body').appendChild(hidden);
hidden.select();
document.execCommand('copy');
I just tested that code and it still works, and copies the text to the clipboard as intended. The only notable different I see is that in the older code, I run execCommand() in the global context, whereas in the new script, it's in a function context. Could this have something to do with it?
So the solution to this was odd. execCommand() can only be triggered by a user event handler, so what I had to do was attach a click listener to the window, then invoke a click event on the hidden node. Because that triggered a click handler, that made it work!

Javascript array shows in console, but i cant access any properties in loops

I really try my damndest not to ask, but i have to at this point before I tear my hair out.
By the time the js interpreter gets to this particular method, I can print it to the console no problem, it is an array of "event" objects. From FireBug I can see it, but when I try to set a loop to do anything with this array its as if it doesn't exist. I am absolutely baffled......
A few things:
I am a newbie, I have tried a for(var index in list) loop, to no avail, I have also tried a regular old for(var i = 0; i < listIn.length; i++), and I also tried to get the size of the local variable by setting var size = listIn.length.
As soon as I try to loop through it I get nothing, but I can access all the objects inside it from the FireBug console no problem. Please help, even just giving me a little hint on where I should be looking would be great.
As for the array itself, I have no problems with getting an array back from PHP in the form of: [{"Event_Id":"9", "Title":"none"}, etc etc ]
Here is my code from my main launcher JavaScript file. I will also post a sample of the JSON data that is returned. I fear that I may be overextending myself by creating a massive object in the first place called content, which is meant to hold properties such as DOM strings, settings, and common methods, but so far everything else is working.
The init() function is called when the body onload is called on the corresponding html page, and during the call to setAllEvents and setEventNavigation I am lost.
And just to add, I am trying to learn JavaScript fundamentals before I ever touch jQuery.
Thanks
var dom, S, M, currentArray, buttonArray, typesArray, topicsArray;
content = {
domElements: {},
settings: {
allContent: {},
urlList: {
allURL: "../PHP/getEventsListView.php",
typesURL: "../PHP/getTypes.php",
topicsURL: "../PHP/getTopics.php"
},
eventObjArray: [],
buttonObjArray: [],
eventTypesArray: [],
eventTopicsArray: []
},
methods: {
allCallBack: function (j) {
S.allContent = JSON.parse(j);
var list = S.allContent;
for (var index in list) {
var event = new Event(list[index]);
S.eventObjArray.push(event);
}
},
topicsCallBack: function(j) {
S.eventTopicsArray = j;
var list = JSON.parse(S.eventTopicsArray);
topicsArray = list;
M.populateTopicsDropDown(list);
},
typesCallBack: function(j) {
S.eventTypesArray = j;
var list = JSON.parse(S.eventTypesArray);
typesArray = list;
M.populateTypesDropDown(list);
},
ajax: function (url, callback) {
getAjax(url, callback);
},
testList: function (listIn) {
// test method
},
setAllEvents: function (listIn) {
// HERE IS THE PROBLEM WITH THIS ARRAY
console.log("shall we?");
for(var index in listIn) {
console.log(listIn[index]);
}
},
getAllEvents: function () {
return currentArray;
},
setAllButtons: function (listIn) {
buttonArray = listIn;
},
getAllButtons: function () {
return buttonArray;
},
setEventNavigation: function(current) {
// SAME ISSUE AS ABOVE
var l = current.length;
//console.log("length " + l);
var counter = 0;
var endIndex = l - 1;
if (current.length < 4) {
switch (l) {
case 2:
var first = current[0];
var second = current[1];
first.setNextEvent(second);
second.setPreviousEvent(first);
break;
case 3:
var first = current[0];
var second = current[1];
var third = current[2];
first.setNextEvent(second);
second.setPreviousEvent(first);
second.setNextEvent(third);
third.setPreviousEvent(second);
break;
default:
break;
}
} else {
// do something
}
},
populateTopicsDropDown: function(listTopics) {
//console.log("inside topics drop");
//console.log(listTopics);
var topicsDropDown = document.getElementById("eventTopicListBox");
for(var index in listTopics) {
var op = document.createElement("option");
op.setAttribute("id", "dd" + index);
op.innerHTML = listTopics[index].Main_Topic;
topicsDropDown.appendChild(op);
}
},
populateTypesDropDown: function(listTypes) {
//console.log("inside types drodown");
//console.log(listTypes);
var typesDropDown = document.getElementById("eventTypeListBox");
for(var index2 in listTypes) {
var op2 = document.createElement("option");
op2.setAttribute("id", "dd2" + index2);
op2.innerHTML = listTypes[index2].Main_Type;
typesDropDown.appendChild(op2);
}
}
},
init: function() {
dom = this.domElements;
S = this.settings;
M = this.methods;
currentArray = S.eventObjArray;
buttonArray = S.buttonObjArray;
topicsArray = S.eventTopicsArray;
typesArray = S.eventTypesArray;
M.ajax(S.urlList.allURL, M.allCallBack);
//var tempList = currentArray;
//console.log("temp array length: " + tempList.length);
M.setAllEvents(currentArray);
M.testList(currentArray);
M.setEventNavigation(currentArray);
//M.setEventNavigation();
M.ajax(S.urlList.topicsURL, M.topicsCallBack);
M.ajax(S.urlList.typesURL, M.typesCallBack);
}
};
The problem you have is that currentArray gets its value asynchronously, which means you are calling setAllEvents too soon. At that moment the allCallBack function has not yet been executed. That happens only after the current running code has completed (until call stack becomes emtpy), and the ajax request triggers the callback.
So you should call setAllEvents and any other code that depends on currentArray only when the Ajax call has completed.
NB: The reason that it works in the console is that by the time you request the value from the console, the ajax call has already returned the response.
Without having looked at the rest of your code, and any other problems that it might have, this solves the issue you have:
init: function() {
dom = this.domElements;
S = this.settings;
M = this.methods;
currentArray = S.eventObjArray;
buttonArray = S.buttonObjArray;
topicsArray = S.eventTopicsArray;
typesArray = S.eventTypesArray;
M.ajax(S.urlList.allURL, function (j) {
// Note that all the rest of the code is moved in this call back
// function, so that it only executes when the Ajax response is
// available:
M.allCallBack(j);
//var tempList = currentArray;
//console.log("temp array length: " + tempList.length);
M.setAllEvents(currentArray);
M.testList(currentArray);
M.setEventNavigation(currentArray);
//M.setEventNavigation();
// Note that you will need to take care with the following asynchronous
// calls as well: their effect is only available when the Ajax
// callback is triggered:
M.ajax(S.urlList.topicsURL, M.topicsCallBack); //
M.ajax(S.urlList.typesURL, M.typesCallBack);
});
}

return from JS function

basic JS question, please go easy on me I'm a newb :)
I pass 2 variables to the findRelatedRecords function which queries other related tables and assembles an Array of Objects, called data. Since findRelatedRecords has so many inner functions, I'm having a hard time getting the data Array out of the function.
As it currently is, I call showWin inside findRelatedRecords, but I'd like to change it so that I can get data Array directly out of findRelatedRecords, and not jump to showWin
function findRelatedRecords(features,evtObj){
//first relationship query to find related branches
var selFeat = features
var featObjId = selFeat[0].attributes.OBJECTID_1
var relatedBranch = new esri.tasks.RelationshipQuery();
relatedBranch.outFields = ["*"];
relatedBranch.relationshipId = 1; //fac -to- Branch
relatedBranch.objectIds = [featObjId];
facSel.queryRelatedFeatures(relatedBranch, function(relatedBranches) {
var branchFound = false;
if(relatedBranches.hasOwnProperty(featObjId) == true){
branchFound = true;
var branchSet = relatedBranches[featObjId]
var cmdBranch = dojo.map(branchSet.features, function(feature){
return feature.attributes;
})
}
//regardless of whether a branch is found or not, we have to run the cmdMain relationship query
//the parent is still fac, no advantage of the parent being branch since cmcMain query has to be run regardless
//fac - branch - cmdMain - cmdSub <--sometimes
//fac - cmdMain - cmdSub <-- sometimes
//second relationship query to find related cmdMains
var relatedQuery = new esri.tasks.RelationshipQuery();
relatedQuery.outFields = ["*"];
relatedQuery.relationshipId = 0; //fac -to- cmdMain
relatedQuery.objectIds = [featObjId];
//rather then listen for "OnSelectionComplete" we are using the queryRelatedFeatures callback function
facSel.queryRelatedFeatures(relatedQuery, function(relatedRecords) {
var data = []
//if any cmdMain records were found, relatedRecords object will have a property = to the OBJECTID of the clicked feature
//i.e. if cmdMain records are found, true will be returned; and continue with finding cmdSub records
if(relatedRecords.hasOwnProperty(featObjId) == true){
var fset = relatedRecords[featObjId]
var cmdMain = dojo.map(fset.features, function(feature) {
return feature.attributes;
})
//we need to fill an array with the objectids of the returned cmdMain records
//the length of this list == total number of mainCmd records returned for the clicked facility
objs = []
for (var k in cmdMain){
var o = cmdMain[k];
objs.push(o.OBJECTID)
}
//third relationship query to find records related to cmdMain (cmdSub)
var subQuery = new esri.tasks.RelationshipQuery();
subQuery.outFields = ["*"];
subQuery.relationshipId = 2;
subQuery.objectIds = [objs]
subTbl.queryRelatedFeatures(subQuery, function (subRecords){
//subRecords is an object where each property is the objectid of a cmdMain record
//if a cmdRecord objectid is present in subRecords property, cmdMain has sub records
//we no longer need these objectids, so we'll remove them and put the array into cmdsub
var cmdSub = []
for (id in subRecords){
dojo.forEach(subRecords[id].features, function(rec){
cmdSub.push(rec.attributes)
})
}
var j = cmdSub.length;
var p;
var sub_key;
var obj;
if (branchFound == true){
var p1 = "branch";
obj1 = {};
obj1[p1] = [cmdBranch[0].Branches]
data.push(obj1)
}
for (var i=0, iLen = cmdMain.length; i<iLen; i++) {
p = cmdMain[i].ASGMT_Name
obj = {};
obj[p] = [];
sub_key = cmdMain[i].sub_key;
for (var j=0, jLen=cmdSub.length; j<jLen; j++) {
if (cmdSub[j].sub_key == sub_key) {
obj[p].push(cmdSub[j].Long_Name);
}
}
data.push(obj);
}
showWin(data,evtObj) <---this would go away
})
}
//no returned cmdRecords; cmdData not available
else{
p = "No Data Available"
obj = {}
obj[p] = []
data.push(obj)
}
showWin(data,evtObj) <--this would go away
})
})
}
I'd like to have access to data array simply by calling
function findRelatedRecords(feature,evt){
//code pasted above
}
function newfunct(){
var newData = findRelatedRecords(feature,evt)
console.log(newData)
}
is this possible?
thanks!
Edit
Little more explanation.....
I'm connecting an Object event Listener to a Function like so:
function b (input){
dojo.connect(obj, "onQueryRelatedFeaturesComplete", getData);
obj.queryRelatedFeatures(input);
console.log(arr) //<----this doesn't work
}
function getData(relatedFeatData){
var arr = [];
//populate arr
return arr;
}
So when obj.QueryRelatedFeatures() is complete, getData fires; this part works fine, but how to I access arr from function b ?
Post Edit Update:
Due to the way that this event is being hooked up you can't simple return data from it. Returning will just let Dojo call to the next method that is hooked up to onSelectionComplete.
When init runs it is long before findRelatedRecords will ever be executed/fired by the onSelectionComplete event of the well, which is why you were seeing undefined/null values. The only way to work with this sort of system is to either 1) call off to a method like you're already doing or 2) fire off a custom event/message (technically it's still just calling off to a method).
If you want to make this method easier to work with you should refactor/extract snippets of it to make it a smaller function but contained in many functions. Also, changing it to have only one exit point at the end of the findRelatedRecords method will help. The function defined inside of subTbl.queryRelatedFeatures() would be a great place to start.
Sorry, you're kind of limited by what Dojo gives you in this case.
Pre Edit Answer:
Just return your data out of it. Everywhere where there is a showWin call just use this return.
return {
data: data,
evtObj: evtObj
}
Then your newfunct would look like this.
function newfunct(){
var newData = findRelatedRecords(feature,evt);
console.log(newData);
console.log(newData.data);
console.log(newData.evtObj);
}
If you only need that "data" object, then change your return to just return data;.
Also, start using semicolons to terminate statements.

Categories

Resources