Window.location.href not working inside an onmessage event? - javascript

I have an iframe, inside the iframe there is a form, the form is loaded either empty or with errors message or success message, very basic form, no AJAX or Axios, the page refreshes with the form & success or error messages
I am using this iframe inside another website, when my form is being submitted, I have to scroll to it from the window parent, if there an error message I scroll to it for example
Did not implement that completly, because this is not my problem
This a script I added inside the form ( and iframe of course )
<script>
let errors = document.getElementsByClassName("msg-error");
let data = [];
if(errors.length !== 0) {
for(let i = 0; i<errors.length; i++){
data.push(errors[i].classList[1]);
let anchor = errors[i].getElementsByClassName("error-anchor")[0];
anchor.id = (errors[i].classList[1] + "").trim();
}
}
window.onmessage = function(event) {
event.source.postMessage({message: "TESTForm", data}, event.origin);
};
</script>
I am sending a message to the hosting page! for now, if there is error message, their id are going to be sent the the hosting page
On the hosting page I have this :
<script>
// Main page:
window.onmessage = function (event) {
if (event.data.message === "TESTForm") {
let receivedData = event.data.data;
console.log("JOCELYN", receivedData.length === 0);
window.location.href = receivedData.length === 0 ? "#frmTestFrame-1" : "#" + receivedData[0];
}
};
</script>
I receive the message, how I know I am receiving them ?
console.log shows me when the page loads for the first time "JOCELYN true" as a result, & if there is an error it shows me "JOCELYN false"
But in either cases, I have to see an anchor in my url ? right ? in both cases, either true or false! But I don't see it at all, that my original url
Why ? I don't know, and that's why I am here!
Any help would be much appreciated really!

Related

Javascript: Modern working approach to refresh the page redirects to another location

I have tried some answers on SO, but they are old and no longer work, or are blocked by the browser.
I have a login, and a separate login failed page (separate for a good reason, please don't say make them the same page).
If the user is shown the failed login page, and they hit refresh, I would like to go to the login page, not refresh the failed login page.
Existing answers go something like this:
<script type="module">
function redirect()
{
window.location.href="/login/";
return true;
}
window.addEventListener('beforeunload', redirect);
</script>
Not ideal, but instead of catching the reload before navigating away, catch the reload at the start of the page:
Reload detection code from: https://stackoverflow.com/a/53307588/5506400
<script type="module">
const pageAccessedByReload = (
(window.performance.navigation && window.performance.navigation.type === 1) ||
window.performance
.getEntriesByType('navigation')
.map((nav) => nav.type)
.includes('reload')
);
if (pageAccessedByReload) {
window.location.href="/login/";
}
</script>
let me know this will help or not, use type value to check for reload
// Use getEntriesByType() to just get the "navigation" events
var entries = performance.getEntriesByType('navigation');
for (var i = 0; i < entries.length; i++) {
console.log('= Navigation entry[' + i + ']');
var p = entries[i];
// dom Properties
console.log(
'DOM content loaded = ' +
(p.domContentLoadedEventEnd - p.domContentLoadedEventStart)
);
console.log('DOM complete = ' + p.domComplete);
// other properties
console.log('type = ' + p.type);
}

File Picker Sidebar Communication Errors

I'm working on a standalone script that will eventually be published as an add-on. It will have a sidebar users interact with and from there they can launch the Google File Picker to upload a report for processing. I previously was looking for a way for the sidebar to know that the file picker was done. I replicated this answer successfully and got the File picker dialog to send a message back to the sidebar indicating it was finished. However, the console is full of some errors and warnings that I'm wondering if I should be concerned about. The errors are:
Unsafe attempt to initiate navigation for frame with origin
'https://docs.google.com' from frame with URL
'https://***.googleusercontent.com/userCodeAppPanel'.
The frame attempting navigation of the top-level window is sandboxed,
but the flag of 'allow-top-navigation' or
'allow-top-navigation-by-user-activation' is not set.
DOMException: Blocked a frame with origin
"https://###.googleusercontent.com"
from accessing a cross-origin frame.
at findSideBar (https://###.googleusercontent.com/userCodeAppPanel:80:18)
at w0.pickerCallback [as Fb] (https://###.googleusercontent.com/userCodeAppPanel:98:11)
at w0._.h.iV (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:740:393)
at d (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:215:143)
at b (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:210:1)
Refused to get unsafe header "X-HTTP-Status-Code-Override"
The first error I have seen before, however, the second is new and relevant to sending the message from the file picker dialog back to the sidebar.
It's worth mentioning that everything still works. My question is primarily are these errors I should be worried about, will they cause problems when the add-on is being reviewed before being published, and how can I correct them?
I've included below my code for the sidebar creation, picker creation, and the relevant code for sending the message from the picker to the sidebar.
Sidebar Creation:
function buildSidebar(type) {
hideColumns();
hideRows();
hideSheets();
if(type == 'setup' || checkSetup()) {
var html = HtmlService.createTemplateFromFile('SidebarSetupHTML')
.evaluate()
.setTitle('Test');
} else {
var html = HtmlService.createTemplateFromFile('SidebarMainHTML')
.evaluate()
.setTitle('Test');
}
SpreadsheetApp.getUi().showSidebar(html);
}
Picker Creation:
function showPicker() {
var html = HtmlService.createHtmlOutputFromFile('PickerHTML.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, 'Select Report(s)');
}
Picker Message Code:
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
(function findSideBar(limit) {
let f = window.top.frames;
for (let i = 0; i < limit; ++i) {
try {
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
} catch (e) {
console.error(e);
continue;
}
}
})(10);
}
}
Sidebar launch picker and receive message:
function testPicker() {
google.script.run.withSuccessHandler(pickerResponse).showPicker();
}
function pickerResponse(e) {
(async () => {
let receiver = new Promise((res, rej) => {
window.modalDone = res;
});
var message = await receiver;
console.log('message received');
if(message == 'PICKED' || message == "NOT_PICKED") {
console.log(message);
}
//Do what you want here
})();
}
When I saw your script, I thought that the reason for your issue might be the loop after the if statement was true. So for example, when the if statement is true, how about putting break in the last line? So, how about the following modification?
From:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
To:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
break; // <--- Added
}
Note:
When I tested a following sample script for a custom dialog on Google Docs (For example, it's Google Spreadsheet),
<script>
google.script.host.close();
alert("ok");
</script>
I confirmed that the alert window is opened and also, the dialog is closed. So I thought that google.script.host.close() might work with the asynchronous process.
For example, when google.script.run with the high process cost is used as follows,
<script>
google.script.host.close();
google.script.run.withSuccessHandler(_ => alert("ok2")).myFunction();
alert("ok1");
</script>
it seems that myFunction() and alert("ok2") are not run because the dialog is closed before run myFunction(), while alert("ok1") is run. On the other hand, when the process cost is low, it seems that the script after google.script.host.close() is run.
From these situation, as an attempt, I have proposed to add break after google.script.host.close() for OP's issue.
Reference:
Loops and iteration

Go to the given URL (if we not there), show the form and scroll to it

So I have this piece of code where the form is hidden until I click on the element. If I'm in that page (profile.php) it shows the form and scrolls down to it but if I'm not on that page (e.g: index.php) it goes to thatpage(profile.php) but doesn't show the form and doesn't scroll down to it until I click again on that element (which it is in the menu)
So here is my html code:
<a id='showForm'>Apply</a>
<div class="formL" style="display:none">
//code
</div>
and here's my script:
<script>
$('#showForm').click(function() {
let currentURL = $(location).attr("href");
let redirectURL = "http://127.0.0.1/dealaim/profile.php#formL"
if (currentURL !== redirectURL) {
$(location).attr("href", redirectURL);
var formL = $('.formL').show();
document.documentElement.scrollTop = formL[0].offsetTop;
} else {
var formL = $('.formL').show();
document.documentElement.scrollTop = formL[0].offsetTop;
}
</script>
The code after your redirect won't do anything; that code needs to run on the page you redirect to, not the page doing the redirect.
Once the redirect takes place, it's a question of passing along some info that the page can receive and use to automatically invoke the function. So:
$('#showForm').click(function() {
let currentURL = location.href;
let redirectURL = "http://127.0.0.1/dealaim/profile.php?showForm=1"
if (currentURL !== redirectURL)
location = redirectURL;
else {
let formL = $('.formL').show();
document.documentElement.scrollTop = formL[0].offsetTop;
}
});
if (location.href.includes('showForm=1') $('#showForm').click();
Note also there's no point at all in doing $(location).attr('href') - you just unnecessarily invoke jQuery and wrap what is a simple object-and-property combo in its API. Just use location.href.

Chrome extension logic is not wοrking

I've managed to get most of my Chrome extension working, but there is a problem I can't work out.
You can grab it here if you want and load it as an unpacked extension.
After loading it works like this.
You are prompted that they need to enter a URL on the options page.
You enter a URL (e.g. http://example.com) on the options page as asked and click save, and then when you click the icon in the toolbar you can see the web page appear in the popup.
If you then go and removes the URL from the options page and clicks save, then the popup does not show the original prompt page they saw at the beginning.
I think this code (from popup.js) is at fault, but I can't see why it won't work.
var url = localStorage.url;
var alturl = chrome.extension.getURL("need-to-enter-url.html");
var element = document.getElementById("testerURL");
if (url != undefined || url != null) {
element.src = url;
} else {
element.src = alturl;
};
When you "remove" the url you are actually saving an empty string. localStorage.url = "" so your value checking is failing. I would also recommend tweaking the if logic to be clearer.
Use something like this:
if (url === undefined || url === null || url === "") {
element.src = alturl;
} else {
element.src = url;
}
Optionally you can rely on JavaScript's truthiness.
if (url) {
element.src = url;
} else {
element.src = alturl;
}

'load' event not firing when iframe is loaded in Chrome

I am trying to display a 'mask' on my client while a file is dynamically generated server side. Seems like the recommend work around for this (since its not ajax) is to use an iframe and listen from the onload or done event to determine when the file has actually shipped to the client from the server.
here is my angular code:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
e.load(function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
});
angular.element('body').append(e);
This works great in Firefox but no luck in Chrome. I have also tried to use the onload function:
e.onload = function() { //unmask here }
But I did not have any luck there either.
Ideas?
Unfortunately it is not possible to use an iframe's onload event in Chrome if the content is an attachment. This answer may provide you with an idea of how you can work around it.
I hate this, but I couldn't find any other way than checking whether it is still loading or not except by checking at intervals.
var timer = setInterval(function () {
iframe = document.getElementById('iframedownload');
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// Check if loading is complete
if (iframeDoc.readyState == 'complete' || iframeDoc.readyState == 'interactive') {
loadingOff();
clearInterval(timer);
return;
}
}, 4000);
You can do it in another way:
In the main document:
function iframeLoaded() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
}
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element('body').append(e);
In the iframe document (this is, inside the html of the page referenced by url)
window.onload = function() {
parent.iframeLoaded();
}
This will work if the main page, and the page inside the iframe are in the same domain.
Actually, you can access the parent through:
window.parent
parent
//and, if the parent is the top-level document, and not inside another frame
top
window.top
It's safer to use window.parent since the variables parent and top could be overwritten (usually not intended).
you have to consider 2 points:
1- first of all, if your url has different domain name, it is not possible to do this except when you have access to the other domain to add the Access-Control-Allow-Origin: * header, to fix this go to this link.
2- but if it has the same domain or you have added Access-Control-Allow-Origin: * to the headers of your domain, you can do what you want like this:
var url = // url to my api
var e = angular.element("<iframe style='display:none' src=" + url + "></iframe>");
angular.element(document.body).append(e);
e[0].contentWindow.onload = function() {
$scope.$apply(function() {
$scope.exporting = false; // this will remove the mask/spinner
});
};
I have done this in all kinds of browsers.
I had problems with the iframe taking too long to load. The iframe registered as loaded while the request wasn't handled. I came up with the following solution:
JS
Function:
function iframeReloaded(iframe, callback) {
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframeReloaded(iframe[0], function () {
console.log('Reloaded');
})
JQuery
Function:
$.fn.iframeReloaded = function (callback) {
if (!this.is('iframe')) {
throw new Error('The element is not an iFrame, please provide the correct element');
}
let iframe = this[0];
let state = iframe.contentDocument.readyState;
let checkLoad = setInterval(() => {
if (state !== iframe.contentDocument.readyState) {
if (iframe.contentDocument.readyState === 'complete') {
clearInterval(checkLoad);
callback();
}
state = iframe.contentDocument.readyState;
}
}, 200)
}
Usage:
iframe.iframeReloaded(function () {
console.log('Reloaded');
})
I've just noticed that Chrome is not always firing the load event for the main page so this could have an effect on iframes too as they are basically treated the same way.
Use Dev Tools or the Performance api to check if the load event is being fired at all.
I just checked http://ee.co.uk/ and if you open the console and enter window.performance.timing you'll find the entries for domComplete, loadEventStart and loadEventEnd are 0 - at least at this current time:)
Looks like there is a problem with Chrome here - I've checked it on 2 PCs using the latest version 31.0.1650.63.
Update: checked ee again and load event fired but not on subsequent reloads so this is intermittent and may possibly be related to loading errors on their site. But the load event should fire whatever.
This problem has occurred on 5 or 6 sites for me now in the last day since I noticed my own site monitoring occasionally failed. Only just pinpointed the cause to this. I need some beauty sleep then I'll investigate further when I'm more awake.

Categories

Resources