SharePoint 2013 App: 'Object' not defined (IFrame / IE9) - javascript

When developing an (SharePoint hosted) App Part for SharePoint 2013 I continiously get the error message 'Object' not defined. On other occassions (depending on which external .js file I'm loading) I might get an 'Function' not defined error. To be more specific: I only get this error, when I'm adding the App Part to a (wiki) page. The page is loaded in IE9 in Standards mode (but the error also happens in Compat View).
I found the following Information at Microsoft: APIs Are Not Available if iFrame Is Removed from DOM Tree => http://msdn.microsoft.com/en-us/library/gg622929(v=VS.85).aspx?ppud=4
I've added a simple alert to the head of the HTML page I'm trying to load as the source of the App Part (which is in fact a simple IFrame object) and indeed the page is loaded multiple times when adding the App Part. Once the App Part is added, the alert is only triggered once and I don't experience any 'Object' not defined errors. So I suspect that my App Part is first created and then manipulated several times (being attached and detached from the DOM).
It seems that my App Part, because it's temporarily removed from the DOM removes the JavaScript API! If that is the case, then how can I develop App Parts that rely on JavaScript (and shouldn't they not rely on JavaScript in the first place?!) ...

I can "surpress" this behaviour if I reload the page untill the JavaScript API is available again (this seems to be after the last time the IFrame was detached and attached again). I therefore simply put the following script as the first statement in the page's head:
<script type="text/javascript">
// Work-around for IE9 (http://msdn.microsoft.com/en-us/library/gg622929(v=VS.85).aspx?ppud=4)
if (typeof Object == 'undefined' || typeof Function == 'undefined') {
window.location.reload();
}
</script>
Still, this "pre-emptive" collection of garbage to me seems a bug rather than a feature!

Related

How to prevent Google Tag Manager overwriting document.write()?

We are using Angular for our Website. As not all Pages have been ported to Angular, we implemented a hybrid approach:
Every request goes to Angular first. When it has been loaded, it checks if the Route exists
If not, the HTML-page is fetched from the backend
The html-Element in the DOM (i.e. the complete page) is replaced with the response's body
ngOnInit() {
this.railsService.fetchRailsPage(this.router.url).subscribe(
(response) => this.replaceDOM(response),
(errorResponse) => this.replaceDOM(errorResponse.error)
);
}
private replaceDOM(newContent: string) {
document.open();
document.write(newContent);
document.close();
}
Since all a-hrefs in old pages are plain old hrefs (not Angular's routerLinks), once the user navigates away, the page is reloaded and Angular kicks in again.
So far, it works, but: I noticed that sometimes the DOM is not replaced with the response body.
Debugging brought us to the conclusion that Google Tag Manager could be the issue. It overwrites document.write() and a lot of other default Javascript functions.
Why is that? And how can this be prevented to get the default version of e.g. document.write()?
Seconding Alan here.
Please make sure you're running two tests:
Block gtm with the request blocking function of the dev tools and try reproducing the issue.
Try creating an empty GTM container, loading it on page and reproduce the issue.
If the first test shows that The issue persists with GTM blocked, then it's not GTM.
If the second test shows that the issue is solved, then it's not about GTM but about the logic used in it's configuration.
If anything, I would first make sure no custom code in GTM additionaly overrides document.write (which I've never seen before, but it's definitely possible). Then I would broadly audit all custom scripts deployed by GTM. After that, I would try pausing all the element visibility triggers if any are deployed and seeing if that helps.
GTM likely would aim to override write to be able to watch DOM changes. But it does so gently, adding a bit of tracking there and not changing the essence of it. It's severely unlikely that GTM's core logic would conflict with Angular.
//UPD just had a chat with a colleague on Measure. It looks like the only scenario when GTM overrides the document.write is when there are Custom HTML tags that have an option to "support document.write". The Element Visibility trigger uses mutation and intersection observers rather than listening to document.writes.

Javascript acting differently in inappbrowser vs Chrome - Undefined global variables

My problem is that when I use inappbrowser to open my web page, the main javascript file seems to be ignored, even though it is referenced in the Head of the file that I am opening using ref = cordova.InAppBrowser.open('https://MYURLHERE:8484/home/login', '_self', 'location=yes');
I am using chrome://inspect/#devices to inspect the console and I get errors when hitting buttons.. Anything referenced in the main.js file is undefined.. whereas the actual code for the autocomplete and button seems to be executing.
Please note The url is working fine when viewed in the chrome browser. The javascript files seem to load correctly, and all variables are defined correctly. - It is only when I use inappbrowser that I see issues with undefined variables.
My code (cordova app):
ref = cordova.InAppBrowser.open('https://MYURLHERE:8484/home/login', '_self', 'location=yes');
var myInAppBrowserCallback = function(event) {
console.log(event.url, 'LOADED');
};
ref.addEventListener('loadstart', myInAppBrowserCallback);
//Works fine.
My code (from the actual website I'm trying to view in Inappbrowser):
<script src="/Scripts/commonFunctions.js"></script>
var p = 'Hello';
<script src="/Scripts/loginPage.js"></script>
$('#btnLogin').on('click', function() {
alert(p);
});
Returns an alert with undefined.
What I have tried:
Extensive googling! (all the answers seem to be related to javascript not being enabled.. or 404 errors or things like that.. Not with global variables from one js file being undefined in another js file.)
Removing inappbrowser, using window.open.. Won't work as the app needs to execute scripts to inspect localstorage of the site.
Re-installing the android platform using cordova.
Re-installing the plugin.
Checking chrome in the inspector to ensure the files are in sources tab (they are).
Checking that the functions in main.js also exist (they do).
Thanks, JFIT
Summary:
The problem was that a javascript file was trying to load 'SpeechSynthesis' which was undefined only in the inappbrowser.
Details:
The problem with this was confusing but here are the steps I found to debug / solve:
Set up Remote Debugging on the phone so that you can view the console of the phone using chrome://inspect/#devices
View the console of the inappbrowser site (which will show as a seperate page to the inappbrowser instance.
Identify which files are being loaded and remove files until 'undefined' errors disappear.
Add back in the most recent file and start to strip out various functions (especially functions/variables before document.ready (global variables also)
Find the line/variable that way. It was handy for me this way as the actual undefined variable showed up after I removed most of the files.
The actual cause of my error was the speechSynthesis functionality.
The speechsynthesis lines were removed, and I readded all remaining files and everything works. This function works fine in chrome on desktop/android but not in the inappbrowser.

Dojo timing issue with dijit/registry and dojo/domReady

I am working on a one-page application in Dojo which submits forms via ajax and returns parses the return value before rendering the page. When I go about this I end up with a timing error.
When the following code is included via a script tag, it logs undefined:
require(["dijit/registry", 'dojo/domReady!'], function(registry){ console.log(registry.byId('my-id')) });
When I paste it in the console, I get the expected dijit widget.
I suspect that the problem is that this is firing before the page is rendered. Is there an easy way to ensure that this happens after the current document has been fully parsed and included in the main window?
May be you should try, to add Parser and call the Parser.parse() before trying to access the widget.
This is from Dojo documentation
Note that waiting for dojo/domReady! to fire is often not sufficient
when working with widgets. Many widgets shouldn’t be initialized or
accessed until the following modules load and execute:
dojo/uacss
dijit/hccss
dojo/parser

Exception in defer callback prevents iron-router from rendering when in an iframe on FireFox

I'm working on Meteor app that mostly expects to exist within an <iframe>.
Steps to reproduce
meteor create iframe-test
cd iframe-test
meteor add iron:router
meteor
Create a test page using jsbin.com or some other tool that loads http://localhost:3000 in an iframe.
Load the page in Firefox and take a look at the console. You'll see something like this (note you might need to hard-refresh with Ctrl/Cmd+Shift+R).
I've added links to an example jsbin and git repo below.
18:26:56.356 Exception in defer callback: onLocationChange#http://localhost:3000/packages/iron_router.js?7b16fe70bccff9182aca02afb2ccd7708ac57c64:2227:55
Tracker.Computation.prototype._compute#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:323:41
Tracker.Computation#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:207:41
Tracker.autorun#http://localhost:3000/packages/tracker.js?6d0890939291d9780f7e2607ee3af3e7f98a3d9c:564:48
Router.prototype.start#http://localhost:3000/packages/iron_router.js?7b16fe70bccff9182aca02afb2ccd7708ac57c64:2223:68
Router/ eval:23:19959
1 meteor.js:944:47
The exception stops iron-router from rendering any template (the page just appears blank).
Exception only occurs on first load - if you refresh the page using a soft refresh (Ctrl/Cmd+R) then templates get rendered correctly.
The issue can be reliably reproduced with a hard refresh (Command/Ctrl+Shift+R)
Exception only happens on FireFox (running 40.0a2) - fine in other browsers.
Appears to be coming from the Meteor.defer call on line 44 of iron-router/lib/router.js (in the iron-router repository)
Repro repos
https://github.com/joeapearson/iframe-test (meteor app)
http://jsbin.com/qucuberupi/edit?html,output (jsbin with an iframe)
Any ideas about what could be going on?
Thanks for any help!
For reference, I've reported this as a potential issue here although I'm not necessarily convinced the issue lies in iron-router itself at all. I have a suspicion that this is somehow related to the HTML5 History API implementation in FireFox itself, which behaves differently in an <iframe> for some reason.
Workaround
Looks like it's possible to workaround this issue by dynamically creating the <iframe> before setting its location like this:
var body = document.getElementsByTagName('body')[0];
var iframe = document.createElement('iframe');
body.appendChild(iframe);
iframe.src = 'http://localhost:3000';
Still think this is something related to how FireFox is setting up the HTML5 history in the <iframe>. If you query history.state in the two differing iframes you get these results:
Dynamically created <iframe>
history.state // Object { initial: true }
Statically created <iframe>
history.state // null

Javascript in asp.net MVC... Beginner issue

I created an Asp.Net MVC Internet Aplication and in my Index view of the Home Controller I have this
This is the first line, before the script results.
<script type="text/javascript" src="~/Script/Teste.js"></script>
<br />
This line comes after the script.
In my Teste.js I have this:
document.write("Yes! I am now a JavaScript coder!");
But nothing happens. If I change the src attribute and put some random name src="aaaa", despite the fact "aaaa" doesnt exist, I get no error in runtime.
EDIT
Also, check your path again. The default MVC templates in VS create a folder called Scripts, not Script. ("~/Scripts/teste.js")
Per the comment below, this was not the root cause of the issue, but in other cases can easily bite new JavaScript developers.
Most likely, your document.write function is firing before the document is ready, leading to the appearance that nothing is happening. Try the following in your Teste.js file
window.onload = function ()
{
document.write("Yes! I am now a JavaScript coder!");
//or even better as a test
alert("This alert was called");
}
Check the source of your page as well, it could be the document is being written to, you just can't see it due to markup/page styling.
As for you second issue, there will be no 'Runtime Exception' thrown if you reference a non-existent file. If you are using tools like Firebug or Chrome's developer tools, you should see a request to http://siteDomain/Scripts/aaaa.js with a response of 404, not found.
You generally should avoid using document.write() unless you absolutely have to use it for some reason... I don't think I've ever come across such a situation, and write a lot of Javascript.
Try this:
1) Put this in your HTML:
<script src="/scripts/teste.js"></script>
2) Put this in your JS:
alert('Yes! I am now a JavaScript coder!');
3) Open Chrome since it makes it easy to look for external resources loading and open the Network tab in Developer Tools (click the menu button at top-right, Tools > Developer Tools, Network tab).
4) Run your project and copy/paste the URL in the browser that comes up into this Chrome window, and hit enter.
When your page loads one of 2 things will happen:
A) You'll get the alert box you wanted or
B) You'll find out why it isn't loading because the Network tab will show the browser attempting to fetch teste.js and failing in some fashion, for example a 404, which would indicate you've got a typo in the path, or the script isn't where you thought it was, etc.
Put the following line at the very end of your document. There should not be anything after. Then try to load the page.
<script type="text/javascript" src="~/Script/Teste.js"></script>
Also, try pressing F12 once the page loads to see the source. Check if you script is there.
In MVC, the tilde is used to refer to the root URL of your application. However, it cannot normally parse this information. If you write:
<script src="~/Script/Teste.js"></script>
The lookup will fail, because the ~ means nothing special in HTML. If you're using Razor as your view engine (not ASPX), you need to wrap that call in Url.Content like so:
<script src="#Url.Content(~/Script/Teste.js)"></script>
Doing this will ensure a valid URL is provided to the browser.
With that in mind, you need to check that you have the file name and folder name both correct. You also need to ensure that the file is being deployed with your application. You can do this my opening the properties panel while the file is selected in the Solution Explorer and pressing F4.

Categories

Resources