I'm trying to collect ratings for all products that have ratings on this page: https://www.theluxelens.com/pages/photoshop-overlays. While I can get the code below to work in my own browser (Chrome), it does not work on the page itself.
It is, however, able to get the elements that have the ratings, because the first console.log statement returns those elements.
var ratingsElements = document.getElementsByClassName("spr-badge");
console.log(ratingsElements);
var nonZeroRatings = [];
for(var i = 0; i < ratingsElements.length ; i++){
var rating = ratingsElements[i].getAttribute("data-rating");
console.log(rating);
if(rating != "0.0") {
nonZeroRatings.push(rating)
}
}
console.log("logging the ratings...");
console.log(nonZeroRatings);
There looks like a slight difference in what is returned when the code below is run in my own console vs from the page itself. When run from my browser console, the first console.log statement in my code returns an HTMLCollection that is slightly different - I believe this difference is why the code isn't working when run from the page itself, but I don't know why it is different. This code works, as you can see it delivers the ratings of products that have them (non-zero ratings) in an array:
VS. when the same code, run from the page, notice the HTMLCollection returned is slightly different:
This is the full script tag from the page:
<script type="text/javascript">
window.onload = function () {
var ratingsElements = document.getElementsByClassName("spr-badge");
console.log(ratingsElements);
var nonZeroRatings = [];
for(var i = 0; i < ratingsElements.length ; i++){
var rating = ratingsElements[i].getAttribute("data-rating");
console.log(rating);
if(rating != "0.0") {
nonZeroRatings.push(rating)
}
}
console.log("logging the ratings...");
console.log(nonZeroRatings);
};
</script>
Thanks for any insight here. This is a Shopify website, if that makes a difference.
When you log something from the page, it basically logs a reference which values are not filled in until the developer goes to look at the console entry. You may notice that little information icon beside it in the log entry, if you hover/click on that it should indicate this to you. Where, as when you're running this from the console it instead decides in this case to pull the collection data right away and log it.
That's the difference between runtime execution and developer intent in the console in some situations. This same concept applies at other times as well, I don't know all of them right off. That information icon showing up is a good indicator of the entry being a reference initially instead of a full data copy.
Related
I am working on a Preact-CLI project with a Preact-Router and it works fine on the localhost. But the production doesn't work well after the build.
I have created a one page object which gets its content dynamically from a JSON file (inside the project not external). So I've loaded the same page object 2 times for each different page.
I get the page url (using this.props.permalink) and compare it with the JSONObject.title. If they are the same I want to get the corresponding JSON content to display it on the corrrct page. Works like a charm on localhost, but not in production.
Issue:
Somehow all pages get the content of the first JSON element. First I thought it was a server issue but I was wrong. The builded files are wrong after the prerendering/build. So the prerendered html of page B contains the content of the prerendered page A.
My guess is that during the build this.props.permalink doesn't work. How should I handle this?
Additional info:
I use the prerender function but not the service worker for the build.
Thanks!
UPDATE:
I have rewritten the function. I guessed I needed to set the dynamic content through a loop, so that during the build the compiler loops through it and is able to prerender all the pages.
The iteration and setting the state works, but only the final element of the PrerenderUrls array gets stored. So now all pages gets the JSON content of the first element.
componentWillMount() {
for (var i = 0; i <= PrerenderUrls.length; i++) {
// the code you're looking for
let removeDash = new RegExp("-")
var needle = PrerenderUrls[i].title
var needle1 = needle.replace(removeDash, " ")
alert("1")
// iterate over each element in the array
if (needle1 != "Homepage") {
for (var x = 0; x < Data.length; x++) {
// look for the entry with a matching `code` value
let removeDash = new RegExp("-")
var nodash = Data[x].title.replace(removeDash, " ")
var nocaps = nodash.toLowerCase()
if (nocaps == needle1) {
alert("needle2: "+ needle1 + " nocaps: " + nocaps)
//alert("data "+ Data[x].title)
this.setState({
pageTitle: Data[x].title,
descShort: Data[x].descShort,
description: Data[x].desc,
img: Data[x].img
})
alert("state "+ this.state.pageTitle)
}
}
}
}
From your description it seems you have a standard Javascript closure problem. I noticed you use both let and var. If let is supported, use it instead of var. It will automagically solve your closure issues, because let creates variables with the block scope, instead of a function scope. Otherwise, you can try to replicate how let does it under the hood - throw the variable to the callback function. Something in the lines of:
// ...
for (var x = 0; x < Data.length; x++) {
try { throw x }
catch(iterator) {
this.setState({
pageTitle: Data[iterator].title
});
}
}
PS. It is very difficult to follow your code, when it is so specific to your functionality. You could simplify it, and focus on the troubling issue. Most of the code you provided is not relevant to your problem, but makes us going through it anyway.
I have a date input in my page, which I'm using Daterangepicker framework to populate it.
Here is the code of how I start my page!
$(function(){
startSelectors();
var variaveis = returnInputVars();
var rede = variaveis[0];
var codLoja = variaveis[1];
var period = variaveis[2];
console.log('1.'+rede+' 2.'+codLoja+' 3.'+period);
});
function returnInputVars(){
var rede = $("#dropdown-parceria").val();
var codLoja = $("#dropdown-loja").val();
var periodo = $("#datepicker-range").val();
return [rede, codLoja, periodo];
};
The function startSelectors() is set to start my datepicker and other fields, which is working perfectly. After it, I create a var called "variaveis" to fill
with the values of each field because I will use then later (this functions also works perfectly at other scripts of my page).
Running the page, my console returns this:
The funny thing is, if I type at the console this, the value is shown, just while starting the script is does not work!
Anybody experienced something like this?
***UPDATE
Adding this script to my start function:
console.log($("#datepicker-range"));
The value is shown, but the second console.log don't:
EDIT 1. FIDDLE (Suggested by #halleron)
To ensure things are loaded in the correct order, it is useful to apply a page sniffer code snippet that will scan the page continuously until a condition is met, or until a preset counter limit is reached (to prevent strain on browser memory). Below is an example of what I typically use that would fit your scenario.
I think because you are dealing with asynchronous loading, you can't have a global variable that holds the values in a global scope without an interval to detect when it can be used. Otherwise, it will attempt to read the variable when it is not yet ready.
You can invoke functions anywhere you like. But I would keep all of your variables contained within the page_sniffer_2017() because that is a controlled environment where you know that everything successfully loaded and you know that the variables are ready to be accessed without error.
That way, regardless of connection speed, your functions will only fire when ready and your code will flow, sequentially, in the right order.
Within the ajax success options, always add a class to the body of the document that you can search on to determine if it has finished loading.
$(document).ready(function() {
page_sniffer_2017();
});
function page_sniffer_2017() {
var counter = 0;
var imgScanner = setInterval(function() {
if ($("#datepicker-range").length > 0 && $("#datepicker-range").val().length && jQuery('body').hasClass('date-picker-successfully-generated')) {
var periodoDatepicker = $("#datepicker-range").val(); // ok
console.log(periodoDatepicker); // ok
var variaveis = returnInputVars(replaceDate(periodoDatepicker)); // ok
console.log(variaveis[0], variaveis[1], variaveis[2]);
//startNewSelectors(variaveis);
// start ajax call
generateData(variaveis[0], variaveis[1], variaveis[2]);
clearInterval(imgScanner);
} else {
//var doNothing = "";
counter++;
if (counter === 100) {
console.log(counter);
clearInterval(imgScanner);
}
}
}, 50);
}
I have a script to create contacts in my database from the contents of my Google Sheet. It first verifies the contact doesn't exist in my database, then adds the contact. I have thousands of contacts, so to reduce the number of contacts in the existing contacts cache, I filter my contact list by state.
var leadsCache = [];
function createContact(leads){
var leadState = '';
for(var i=0; i<leads.length; i++){
if(leads[i].state != leadState){
leadState = leads[i].state;
populateLeadsCache(leadState);
}
var existingLead = leadsCache[leads[i].email];
if(existingLead === undefined){
var leadId = createNewLead(leads[i]);
}
}
}
This works as expected, until I get to a lead with a new state. The code hangs here:
leadState = leads[i].state;
I don't get an error message. I can set the var to empty like this: leadState = '', but I cannot set the value to something else.
In stepping through the code, I can see that leads[i].state has a new string value.
Why can't I change the value? What is the best way to accomplish my desired results?
UPDATE
I wish there was a better error reporting system for Apps Script. Turns out I had an issue in populateLeadsCache (continuous loop) but the system appeared stuck on leadState = leads[i].state;.
Anyone know how to improve the error reporting in Apps Script?
I am answering the question so it can be closed, but leaving the question here in case someone else has a similar issue that isn't really the issue.
Apps Script does not have robust error reporting tools and the debugger failed to highlight the true issue in another part of the code.
When getting a timeout message in Apps Script be aware that it may not have anything to do with where the code appears to break.
For me, the next line of code populateLeadsCache had an issue if the selected state had too many contacts, which resulted in a continuous loop.
I am using DataTables.
What I am trying to do is: by using one of the columns values, get page number, where this value is located.
I have tried this: jumpToData()
BUT this didn't work out. The reason is that
var pos = this.column(column, { order: 'current' }).data().indexOf(data);
in jQuery.fn.dataTable.Api.register('page.jumpToData()' returns value >=0 ONLY if I was placed on page where value was.
For example, I want to detect page where needed value is, but I am staying on another page, so to detect value on... page 3, I need to go to this page and only then I can detect it, which makes no sence at all.
What I need to do, is: by staying on pirst page, using value from another pages, detect those pages numbers and then navigate to them:
$('#Grid_grid').DataTable().page(PageNumber).draw(false);
How can I accomplish that?
EDIT:
Got some idea (several changes in jumpToData()):
jQuery.fn.dataTable.Api.register('page.jumpToData()', function (data, column) {
for (var i = 0; i < this.page.info().pages; i++) {
var test = this.page(i).column(column, { order: 'current' }).data().indexOf(data);
if (test >= 0) {
this.page(i).draw(false);
return this;
}
}
return this;
});
(EDIT 2: idea didn't paid off, no difference)
BUT now I got second issue:
None methods of datatable works in .cshtml page.
For example I need to get overall page count. I doing this:
$('#Grid_grid').DataTable().page.info().pages;
and this return me 0;
Meanwhile, putting it in to console (Chrome F12) works fine (returns 5). Whats the matter?
EDIT 3:
Came up with this:
function LoadPage(value) {
var table = $('#Grid_grid').DataTable();
var pageNumber = table.search(value).page();
table.page(pageNumber).draw(false);
}
Looks promising BUT, I still cant validate it because in console DataTable methods are working, but in .cshtml no. (search() or page() returns nothing).
EDIT 4:
Moved issue to another question
CAUSE
Your new API method page.jumpToData() tries to query all pages data because second argument selector-modifier in column() API method has property page: 'all' by default. As written it will always stay on first page.
SOLUTION
There is original page.jumpToData() plug-in posted by Allan Jardine, creator of DataTables. It works as intended and can be used instead of your modification to avoid unnecessary iterations.
$.fn.dataTable.Api.register('page.jumpToData()', function (data, column) {
var pos = this.column(column, {
order: 'current'
}).data().indexOf(data);
if (pos >= 0) {
var page = Math.floor(pos / this.page.info().length);
this.page(page).draw(false);
}
return this;
});
DEMO
See this jsFiddle for code and demonstration.
NOTES
In the demo above I added console.log("Number of pages", table.page.info().pages); just to demonstrate that API method works. However they may work because I have HTML-sourced data.
If you have Ajax-sourced data, you need to query number of pages only when data has been loaded. Use initComplete option to define a callback function that will be called when your table has fully been initialised, data loaded and drawn.
I'm writing a short script that loops through an array and loads each of the results found although I'm having some issues with it loading. If I alert my results found in each loop I get the right data but when I attempt to load this data, it fails.
var arrayScripts = new Array (
'http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js',
'scripts/tiny_mce/tiny_mce.js',
'scripts/jquery.cycle.lite.js',
'scripts/javascript.default.js',
'scripts/jquery.default.js'
);
for (var i=0; i < arrayScripts.length; i++) {
scripts.add(arrayScripts[i]);
}
scripts.load();
I hope you can see what I'm doing wrong here. It appears to be my code inside of my loop.