Let the user decide how many items in a page - reactjs

Context
We have a table with 155 elements,
I want to build this field on every pages,
'Showing result [from starting item - to item number X ] of itemTotal'
StartingItem = 1 -----> at page 1
ItemNumberX = the user can see the next 25 items
this is the actual pages filter
These are data that I actually have
const minShowData = currentPage === 1 ? 1 : (currentPage - 1) * itemsPerPage;
const itemsAtCurrentPage = currentPage * itemsPerPage;
const maxShowData =
totalCount > itemsAtCurrentPage ? itemsAtCurrentPage : totalCount;
const showElement = [5, 10, 20, 25, 50]; ----> this is the old pagination
we have the data itemsPerPage through a reducer, and represent how many items we will see in page, in the second field risultati per pagina is set to 2.
this is how the variable showElement appear
there is a way with these data to made a filter like
Showing results [1-25, 26-50, 51-75 ...] of 155 ?
I don't need the code to build the component, just understand if there is a way to build the computation inside the field

so you have
const itemsPerPage = 25; //this you can set to be user alterable if you want
const itemsTotal = 155; //this you'd use a function to go get the number of items.
const pageIncrements[];
for (let pageNumber = 1; pageNumber <= itemsTotal / itemsPerPage; pageNumber++) {
pageIncrements += [((pageNumber - 1) * (itemsPerPage) + 1)) + " - " + (pageNumber * itemsPerPage)]
}
if (itemsTotal % itemsPerPage > 0) {
pageIncrements += [(itemsTotal - (itemsTotal % itemsPerPage)) + " - " + itemsTotal]
}
Sorry, haven't worked with react syntax, but the logic should be right.

Related

obfuscated page content while using output cache in mvc 5

I'm developing a web application using MVC5 and I use output cache attribute on one of my actions.
the problem is after multiple page visits, my page will have binary content that looks like this:
page screenshot
there's my source:
[OutputCache(Duration = 86400, Location = OutputCacheLocation.ServerAndClient,VaryByParam = "ID;slug")]
[SeoOptionsFilter]
public ActionResult CourseDetail(int ID, string slug)
{
var courseComplexObject =
_courseRepository.GetCourseDetail(ID, slug);
var course = courseComplexObject.Model;
TempData["breadcrumb"] = BreadcrumbViewBuilder
.New().Home().Courses()
.AddMore(course.CategoryName, "/Category/" + course.CategoryId.ToString() + "/" + course.CategorySlug)
.AddMore(course.Name, "/CourseDetails/" + course.CourceId.ToString() + "/" + course.Slug + ".aspx",
true)
.Build();
//TODO: What if course is null
SetViewBagIfUserIsRegistered(course, ID);
TempData["CourseVideoCatOn"] = GetCourseVideoCategory(ID);
TempData["CourseID"] = ID;
TempData.Keep();
// Response.Cache.SetOmitVaryStar(true);
return View(courseComplexObject.Model);
}
I commented my custom action filter [SeoOptionsFilter]
but the result already has errors.
this error occurs randomly in page visits

script & sheet timing out when trying to print large arrays in google script

Background
I have a function that makes a REST API call using UrlFetchApp in Google Scripts.
But the response only returns 2000 records at a time. If there are more records, there is, in the response, a key called nextRecordsUrl, which contains the endpoint and parameters needed to get the next batch of records.
I use a do...while loop to iterate through, pushing the records into a predesignated array, make the next api call. And when it reaches the last batch of records, it exists the do-while loop, then prints (not sure if that's the right term here) the entire to a Google Sheet.
The code
It looks like this:
function getCampaignAssociations() {
clearPage('CampaignAssociations');
var query = '?q=select+CampaignMember.FirstName,CampaignMember.LastName,CampaignMember.LeadId,CampaignMember.ContactId,CampaignMember.Name,CampaignMember.CampaignId,CampaignMember.SystemModstamp,CampaignMember.Email+from+CampaignMember+ORDER+BY+Email ASC,SystemModstamp+ASC';
try {
var arrCampAssociation = getInfoByQuery(query);
if (arrCampAssociation.records.length < 1) {
throw 'there are no records in this query';
}
var campaignAssoc = [];
do {
Logger.log(arrCampAssociation.nextRecordsUrl);
for (var i in arrCampAssociation.records) {
let data = arrCampAssociation.records[i];
let createDate = Utilities.formatDate(new Date(data.SystemModstamp), "GMT", "dd-MM-YYYY");
let a1 = "$A" + (parseInt(i) + 2);
let nameFormula = '=IFERROR(INDEX(Campaigns,MATCH(' + a1 + ',Campaigns!$A$2:A,0),2),"")';
let typeFormula = '=IFERROR(INDEX(Campaigns,MATCH(' + a1 + ',Campaigns!$A$2:A,0),3),"")';
campaignAssoc.push([data.CampaignId, nameFormula, typeFormula, data.Email, data.FirstName, data.LastName, data.LeadId, data.ContactId, createDate]);
}
var arrCampAssociation = getQueryWithFullEndPoint(arrCampAssociation.nextRecordsUrl);
} while (arrCampAssociation.nextRecordsUrl != null && arrCampAssociation.nextRecordsUrl != undefined);
let endRow = campAssocSheet.getLastRow(),
endColumn = campAssocSheet.getLastColumn(),
nameRange = campAssocSheet.getRange(2, 1, endRow, endColumn),
destRange = campAssocSheet.getRange(2, 1, campaignAssoc.length, campaignAssoc[0].length);
destRange.setValues(campaignAssoc);
sheet.setNamedRange('CampaignAssociation', nameRange);
} catch (e) {
Logger.log(e);
Logger.log(arrCampAssociation);
Logger.log(campaignAssoc);
Logger.log(i);
}
}
Issue
Everything works nicely until it comes to printing the array campaignAssoc to the Google Sheet.
See screenshot of the log below. It contains the endpoint for the next both. Notice the timestamp between the earlier logs and the timestamp between the last endPoint and the log where it timed out.
It seems to me that the issue is that when it comes to the printing of the data, it's having issues. If that's the case, have I overloaded the array? There are a total of over 36400 records.
Second attempt
I've tried resetting the array at each loop and printing the array to Google sheet. This is just 2000 records at each attempt and I've definitely done more rows at 1 time, but that didn't help.
Here's the code for that attempt.
function getCampaignAssociations() {
clearPage('CampaignAssociations');
var query = '?q=select+CampaignMember.FirstName,CampaignMember.LastName,CampaignMember.LeadId,CampaignMember.ContactId,CampaignMember.Name,CampaignMember.CampaignId,CampaignMember.SystemModstamp,CampaignMember.Email+from+CampaignMember+ORDER+BY+Email ASC,SystemModstamp+ASC';
try {
var arrCampAssociation = getInfoByQuery(query);
if (arrCampAssociation.records.length < 1) {
throw 'there are no records in this query';
}
do {
Logger.log(arrCampAssociation.nextRecordsUrl);
var campaignAssoc = [];
for (var i in arrCampAssociation.records) {
let data = arrCampAssociation.records[i];
let createDate = Utilities.formatDate(new Date(data.SystemModstamp), "GMT", "dd-MM-YYYY");
let a1 = "$A" + (parseInt(i) + 2);
let nameFormula = '=IFERROR(INDEX(Campaigns,MATCH(' + a1 + ',Campaigns!$A$2:A,0),2),"")';
let typeFormula = '=IFERROR(INDEX(Campaigns,MATCH(' + a1 + ',Campaigns!$A$2:A,0),3),"")';
campaignAssoc.push([data.CampaignId, nameFormula, typeFormula, data.Email, data.FirstName, data.LastName, data.LeadId, data.ContactId, createDate]);
}
let lastRow = campAssocSheet.getLastRow()+1;
campAssocSheet.getRange(lastRow,1,campaignAssoc.length,campaignAssoc[0].length).setValues(campaignAssoc);
var arrCampAssociation = getQueryWithFullEndPoint(arrCampAssociation.nextRecordsUrl);
} while (arrCampAssociation.nextRecordsUrl != null && arrCampAssociation.nextRecordsUrl != undefined);
let endRow = campAssocSheet.getLastRow(),
endColumn = campAssocSheet.getLastColumn(),
nameRange = campAssocSheet.getRange(2, 1, endRow, endColumn);
sheet.setNamedRange('CampaignAssociation', nameRange);
} catch (e) {
Logger.log(e);
Logger.log(arrCampAssociation);
Logger.log(campaignAssoc);
Logger.log(i);
}
}
So here, each loop took a lot longer. Instead of being 1-2 seconds between each loop, it took 45 seconds to a minute between each and timed out after the 4th loop. See the log below:
How do I fix this?

Loop script for all values of dropdown

I'm looking to make a script that cycles through a dropdown list and creates a pdf for each.
https://docs.google.com/spreadsheets/d/1HrXWkNXT7aEWOXkngiuSX9Sr1F0V4Y_rZH6Eg3mjaJQ/edit?usp=sharing
First I would like to check if B2 is not empty, then if so create pdf and change A2 to the next option until all are complete. I have a basic script but feel free to disregard!
function loopScript() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const interface = ss.getSheetByName("Interface");
var folderID = "###GOOGLE DRIVE FOLDER ID###";
var folder = DriveApp.getFolderById(folderID);
const exportOptions = 'exportFormat=pdf&format=pdf'
+ '&size=A4'
+ '&portrait=true'
+ '&scale=4'
+ '&fith=true&source=labnol'
+ '&top_margin=0.05'
+ '&bottom_margin=0.05'
+ '&left_margin=1.00'
+ '&right_margin=0.25'
+ '&sheetnames=false&printtitle=false'
+ '&pagenumbers=false&gridlines=false'
+ '&fzr=false'
+ '&gid=125740569';
var params = {method:"GET",headers:{"authorization":"Bearer "+ ScriptApp.getOAuthToken()}};
var response = UrlFetchApp.fetch(url+exportOptions, params).getBlob();
const nameFile = "NAME OF FILE" + ".pdf" ;
folder.createFile(response.setName(nameFile));
DriveApp.createFile(response.setName(nameFile));
}
I believe your goal is as follows.
You want to check the cell "B2". When the cell "B2" is not empty, you want to set the value of the dropdown list of cell "A2" to the next value of the list.
For example, when the dropdown list is Joe, Barry, Jane, Fred and the cell "A2" is Barry, you want to set the cell to Jane.
In this case, how about the following modified script?
Modified script:
From:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const interface = ss.getSheetByName("Interface");
To:
const ss = SpreadsheetApp.getActiveSpreadsheet();
const interface = ss.getSheetByName("Interface");
if (interface.getRange("B2").isBlank()) return;
const range = interface.getRange("A2");
const values = [...new Set(range.getDataValidation().getCriteriaValues()[0].getValues().flat())];
const nextValue = values[values.indexOf(range.getValue()) + 1] || values[0];
range.setValue(nextValue);
In this modified script, when the cell "B2" is empty, the script is finished. When the cell "B2" is not empty, the script is run and the cell "A2" is updated and your script for creating the PDF file is run.
Note:
In above modified script, when the dropdown list is Joe, Barry, Jane, Fred and the cell "A2" is Fred, the value of Joe is set. If you want to change this, please modify the above script.
In your current script, url is not defined. Please be careful this.
References:
isBlank()
getDataValidation()
getCriteriaValues()
Issue:
If I understand you correctly, you want to do the following:
For each dropdown in A2, check if the formula in B2 populates any values (based on data from sheet Data).
If any value is populated in B due to the formula, create a PDF file using the value of A2 for the file name (you have achieved this already).
Method 1:
In this case, I'd suggest the following workflow:
Retrieve an array with the accepted values from A2 dropdown (you can use the method used in Tanaike's answer).
Iterate through these values, and for each one, set the A2 value, using Range.setValue.
Call flush in order to update the data in B2 according to the current value in A2.
Check if B2 is blank (using Range.isBlank, for example).
If B2 is not blank, create the drive file.
function loopScript() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const interface = ss.getSheetByName("Interface");
const range = interface.getRange("A2");
const values = [...new Set(range.getDataValidation().getCriteriaValues()[0].getValues().flat())].filter(String);
values.forEach(name => {
range.setValue(name);
SpreadsheetApp.flush();
if (!interface.getRange("B2").isBlank()) {
// CODE FOR CREATING FILE
}
});
}
Method 2:
In the previous method, setValue, flush, getRange and isBlank are used iteratively, greatly increasing the amount of calls to the spreadsheet. This is not the best practice, as it will slow down the script (see Minimize calls to other services), and this will get worse if there are more valid options for the dropdown.
Therefore, since the data this formula is using can be found in sheet Data, I'd suggest using that source data instead of the formula, in order to minimize the calls to the spreadsheet.
In this case, you could follow this workflow:
Get all data in Data at once using Range.getValues.
Get all valid options in the data validation from A2, as in method 1.
For each option, check if there's any row in Data that has this option in column A and a non-empty cell in B.
If there is some data for that option, create the file.
function loopScript() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const interface = ss.getSheetByName("Interface");
const data = ss.getSheetByName("Data");
const DATA_FIRST_ROW = 2;
const dataValues = data.getRange(DATA_FIRST_ROW,1,data.getLastRow()-DATA_FIRST_ROW+1,2).getValues();
const range = interface.getRange("A2");
const values = [...new Set(range.getDataValidation().getCriteriaValues()[0].getValues().flat())].filter(String);
values.forEach(name => {
const optionValues = dataValues.filter(dataRow => dataRow[0] === name);
const nonEmpty = optionValues.some(optionValue => optionValue[1] !== "");
if (nonEmpty) {
// CODE FOR CREATING FILE
}
});
}
Thanks to everyone's help, I ended up using Tanaike's advice and continued on to come up with this:
function sendAll() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var interface = ss.getSheetByName("Interface");
var range = interface.getRange("A2");
// Cell of validation
const values = [...new Set(range.getDataValidation().getCriteriaValues()
[0].getValues().flat())]; // Gets array of validation
var first = values[0];
// 1st cell of validation
var number = values.length - 1;
// Length of validation
range.setValue(first);
// Sets value to first one
for(i = 0;i < number;i++) {
// Loop number of names
if (interface.getRange("B2").getValue().length > 0) {
// Sheet isn't empty
var person = interface.getRange("A2").getValue();
const url = 'MY SHEET URL';
var folderID = "MY FOLDER ID";
var folder = DriveApp.getFolderById(folderID);
const exportOptions = 'exportFormat=pdf&format=pdf'
+ '&size=A4'
+ '&portrait=true'
+ '&scale=4'
+ '&fith=true&source=labnol'
+ '&top_margin=0.05'
+ '&bottom_margin=0.05'
+ '&left_margin=1.00'
+ '&right_margin=0.25'
+ '&sheetnames=false&printtitle=false'
+ '&pagenumbers=false&gridlines=false'
+ '&fzr=false'
+ '&gid=0';
var params = {method:"GET",headers:{"authorization":"Bearer "+
ScriptApp.getOAuthToken()}};
var response = UrlFetchApp.fetch(url+exportOptions, params).getBlob();
const nameFile = person + ".pdf" ;
folder.createFile(response.setName(nameFile));
DriveApp.createFile(response.setName(nameFile));
const nextValue = values[values.indexOf(range.getValue()) + 1] || values[0];
range.setValue(nextValue);
}
else {const nextValue = values[values.indexOf(range.getValue()) + 1] ||
values[0];
range.setValue(nextValue);}
}
}

When importing, find and replace the duplicate, or if there is no duplicate, paste at the bottom of the data set

I am attempting to compile a Database with financial information. To do this I need to import new data for each stock 'ticker' into the Database with a script that searches for the last empty row and pastes the data there.
However, If new data is available for a stock, I would like to paste it over the old in the Database - to avoid duplicates. And if there is no duplicates to continue with the original script to paste it at the next available row.
I think I would just need to tweak my current script to add a Find and Replace for any matches of API ImporterTest!C2 and and tickers in Column DatabaseTest!H:H and if there is to paste the ranges in the script below over it, and an IF function for if there is no match to continue with the original script.
Here is the script I have been working with so far:
//set spreadsheet and sheets
const spreadsheet = SpreadsheetApp.getActive();
const importerSheet = spreadsheet.getSheetByName('API ImporterTest');
const databaseSheet = spreadsheet.getSheetByName('DatabaseTest');
//cash flow
const lastRowColumnValuesCash = databaseSheet.getRange("G:G").getValues()
const lastRowIndexCash = lastRowColumnValuesCash.length - lastRowColumnValuesCash.reverse()
.findIndex(row => row[0] !== '');
//income statement
const lastRowColumnValuesIncome = databaseSheet.getRange("AT:AT").getValues()
const lastRowIndexIncome = lastRowColumnValuesIncome.length - lastRowColumnValuesIncome.reverse()
.findIndex(row => row[0] !== '');
//balance sheet
const lastRowColumnValuesBalance = databaseSheet.getRange("CC:CC").getValues()
const lastRowIndexBalance = lastRowColumnValuesBalance.length - lastRowColumnValuesBalance.reverse()
.findIndex(row => row[0] !== '');
//fundamentals
const lastRowColumnValuesFund = databaseSheet.getRange("DY:DY").getValues()
const lastRowIndexFund = lastRowColumnValuesFund.length - lastRowColumnValuesFund.reverse()
.findIndex(row => row[0] !== '');
//keymetrics
const lastRowColumnValuesMet = databaseSheet.getRange("GE:GE").getValues()
const lastRowIndexMet = lastRowColumnValuesMet.length - lastRowColumnValuesMet.reverse()
.findIndex(row => row[0] !== '');
//profile
const lastRowColumnValuesProf = databaseSheet.getRange("IN:IN").getValues()
const lastRowIndexProf = lastRowColumnValuesProf.length - lastRowColumnValuesProf.reverse()
.findIndex(row => row[0] !== '');
//run
if (databaseSheet.getLastRow() + 15 > databaseSheet.getMaxRows())
databaseSheet.insertRowsAfter(databaseSheet.getMaxRows(),
databaseSheet.getLastRow() + 15 - databaseSheet.getMaxRows()
)
importerSheet
.getRange('F3:J40')
.copyTo(
databaseSheet.getRange(`G${lastRowIndexCash + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet
.getRange('S3:W36')
.copyTo(
databaseSheet.getRange(`AT${lastRowIndexIncome + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet
.getRange('AF3:AJ49')
.copyTo(
databaseSheet.getRange(`CC${lastRowIndexBalance + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet
.getRange('AS3:AW59')
.copyTo(
databaseSheet.getRange(`DY${lastRowIndexFund + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet
.getRange('BF3:BJ62')
.copyTo(
databaseSheet.getRange(`GE${lastRowIndexMet + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet
.getRange('BS3:BW36')
.copyTo(
databaseSheet.getRange(`IN${lastRowIndexProf + 1}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
};
Here is a functional Test Sheet to test it on with example solutions (to produce a new dataset to test, change the cell 'C2' in 'API Importer Test' to another ticker, FB, AAPL, GME for example and hit import.)
https://docs.google.com/spreadsheets/d/1UPAz7ku_AILB9aDvKCVyClvBh_5XskGPgGh0w784PR4/edit?usp=sharing
An Example of the 2 Solutions - Note this sheet was for visualization of the logic only, so the current script would not work on these cells.
Solution:
From my perspective, it doesn't make sense to retrieve so many lastRow indexes. This is the index in the sheet Database where data should be written if no duplicate ticker is found, and all these lastRow indexes should be the same. And at least in the example you provided, that is the case, all these indexes coincide.
Taking that into account, I'd do the following:
Check the last row for column G (since I guess the date will always be populated if the row is populated).
Retrieve the value from API ImporterTest!B3, corresponding to the imported ticker.
Retrieve all current tickers in Database, currently in C3, filtering empty rows with filter.
Check if the ticker to be imported is in current tickers and, if that's the case, what index it is located. You can use indexOf for that.
Retrieve the rowIndex where to write the data to, depending on the index from previous step is -1 or not (duplicate found), and taking into account that data start at row 3 and there are 5 merged cells everytime, so 4 out of 5 values was empty and so filtered out. Because of this, rowIndex should be index * 5 + 3 if a duplicate was found. I used a ternary operator to do that in the sample below: const rowIndex = index !== -1 ? index * 5 + 3 : lastRowIndexCash + 1;
Copy all the different ranges to the same rowIndex, since you want to write all data to the same rows.
Code sample:
const moveValuesOnly = () => {
const spreadsheet = SpreadsheetApp.getActive();
const importerSheet = spreadsheet.getSheetByName('API ImporterTest');
const databaseSheet = spreadsheet.getSheetByName('DatabaseTest');
const lastRowColumnValuesCash = databaseSheet.getRange("G:G").getValues()
const lastRowIndexCash = lastRowColumnValuesCash.length - lastRowColumnValuesCash.reverse()
.findIndex(row => row[0] !== '');
const importerTicker = importerSheet.getRange("B3").getValue();
const tickers = databaseSheet.getRange("C3:C").getValues().flat().filter(String);
const index = tickers.indexOf(importerTicker);
const rowIndex = index !== -1 ? index * 5 + 3 : lastRowIndexCash + 1;
console.log(rowIndex)
if (databaseSheet.getLastRow() + 15 > databaseSheet.getMaxRows()) {
databaseSheet.insertRowsAfter(databaseSheet.getMaxRows(),databaseSheet.getLastRow() + 15 - databaseSheet.getMaxRows());
};
importerSheet.getRange('F3:J40').copyTo(
databaseSheet.getRange(`G${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet.getRange('S3:W36').copyTo(
databaseSheet.getRange(`AT${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet.getRange('AF3:AJ49').copyTo(
databaseSheet.getRange(`CC${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet.getRange('AS3:AW59').copyTo(
databaseSheet.getRange(`DY${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet.getRange('BF3:BJ62').copyTo(
databaseSheet.getRange(`GE${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
importerSheet.getRange('BS3:BW36').copyTo(
databaseSheet.getRange(`IN${rowIndex}`),
SpreadsheetApp.CopyPasteType.PASTE_VALUES,
true
);
};

Three.js touch intersects on mobile

I'e seen this problem mentioned here, however this solution (e.touches[0].pageX and e.touches[0].pageY (instead of e.clientX and e.clientY) isn't working for me.
The hotspots are being selected if you touch them about 10 times, and luckily hit the magic spot, or sometimes if you hit somewhere randomly nearby they're hit, otherwise nothing.
My code:
mouse.x = ( event.touches[0].pageX / window.innerWidth ) ;
mouse.y = - ( event.touches[0].pageX / window.innerHeight );
// This is the mouse clicks that do work
//mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
//mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
var vector = new THREE.Vector3( mouse.x, mouse.y, 1 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = ray.intersectObjects( targetList, true );
if ( intersects.length > 0)
hotspotClick(intersects[ 0 ].object);
EDIT
I'm making a bit of progress with this
mouse.x = ( event.touches[0].pageX / window.innerWidth ) * 0.1;
mouse.y = ( event.touches[0].pageY / window.innerHeight ) * 0.1;
However it seems to either be clicking anywhere near the mesh activates it, or you have to press in a certain spot somewhere in the middle of it. So I assume that multiplier is out.
X seems to be OK, Y seems to be the problem.
Can anyone help?
I have tested it on an iPad with both Chrome and Safari and on Android with Chrome.
document.body.appendChild(renderer.domElement);
document.addEventListener('touchend', onDocumentTouchEnd, false);
function onDocumentTouchEnd(event) {
event.preventDefault();
mouse.x = (event.changedTouches[0].clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.changedTouches[0].clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(yourObject3D);
}
It started to work well after appending renderer.domElement.
Before I was using event.Touches with mixed results.
This worked
mouse.x = +(event.targetTouches[0].pageX / window.innerWidth) * 2 +-1;
mouse.y = -(event.targetTouches[0].pageY / window.innerHeight) * 2 +
1;
window.innerwidth has to be window.innerWidth!
Use below code to get correct intersects
event.preventDefault();
var rect = Canvas_Instance.getBoundingClientRect();
mouse.x = + ( (event.targetTouches[ 0 ].pageX - rect.left) / rect.width ) * 2 - 1;
mouse.y = - ( (event.targetTouches[ 0 ].pageY - rect.top) / rect.height ) * 2 + 1;

Resources