IE8 crashes with AngularJS and dynamic content - javascript

I have the following code:
<blockquote class='mt20'>
<p><span>“</span><span>{{iq.quote}}</span><span>”</span></p>
<footer><cite class="dark-grey">{{iq.author}}</cite></footer>
</blockquote>
For some reason, this is causing IE8 to crash. I've done a lot of debugging and have found that when the the iq object contains just the quote:
{quote:"some quote"}
the browser doesn't crash. It only crashes with both quote and author.
I use a special function to get my data. It looks like:
this.get = function(){
var arr = {};
if(!arr.length){
$http.get('url').success(function(data){
$.extend(arr, data);
});
}
return arr;
}
I use this because the object is automatically bound so I don't have to watch it. It appears that the crash happens on the extension of the data to the object when the view tries to update. Any thoughts?

I was able to fix this by changing from content bindings using {{}} to ng-bind.
<blockquote>
<p><span>“</span><span ng-bind="iq.quote"></span><span>”</span></p>
<footer><cite ng-bind="iq.author"></cite></footer>
</blockquote>
I have a hypothesis that the HTML5 footer element, combined with the {{}} content binding and the dynamic update caused a memory leak that crashed IE8 but I can't prove this. I do know that when the content is set and not changed (like what would happen if you used $http.get instead of my method), the browser doesn't crash. I also was using:
var obj = {}
and:
delete obj.key
I changed this to:
var obj = new Object()
based on this post: https://markfeimer.wordpress.com/2014/05/23/internet-explorer-8-javascript-delete-keyword-bug/

Related

Accessing elements added to DOM with ng-bind-html

This probably has been answered before but I have tried for 48 hours now to find an answer with now luck. Posting this is a last resort.
So I have this Tetris clone and it works as seen here: http://www.crawfordcomputing.com/portfolio/Jetris.php
But it does not work in this single page angular app:
http://www.crawfordcomputing.com/AkadineWebOS/
I am re-coding the whole site to be a AngularJS single page app. To this end, I load the html via ng-bind-html and the compile directive found here: AngularJS directive in ng-bind-html.
The html string I am binding is exactly: EDIT: tried making canvas a childnode, did not work.<div><canvas id='gamewindow' width='780px' height='560px'></canvas></div>
The necessary scripts are loaded via the function found here: Single page application - load js file dynamically based on partial view
Note: html5jetris.js is actually an object: var jetris = { startgame: function(), ...};
Using firebug, I can see all added elements and the scripts in the DOM. I also am assuming that being a single page already loaded that I cannot use window.onload = jetris.startGame; after the closing ";" of var jetris = {};
1) I tried putting an alert("hello"); as second to last line (just before the call to startGame) and tried stepping through with firebug. It appears that html5jetris.js, while loaded, is not running.
2) I tried to access the canvas by id directly from address bar with javascript:getElementByID("gamewindow").innerHTML = "hello";
no success; this was several seconds after the ng-bind-html executed well past the 1-1.5 seconds it takes angular to load it.
3) if I pre-emptivly load all the js in my index.php, then firefox will step through it. But I only want to load the javascript when needed. This does not fix not being able to access the canvas by id.
4) by it self, Jetris works, just not loaded dynamically by AngularJS.
Note: Jetris does not require jQuery, even though it's there for Angular. It depends on my own utitlty.js (made as part of a javascript class, converted to object: var U = {};) and my own html5canvas.js (again, var canvas = {};) Since these are objects, the variables in them should not bump heads with anything jQuery or Angular.
Any ideas?
here is the directive:
.directive('compile', ['$compile', function ($compile) {
return function(scope, element, attrs) { //taken from Joël (Thank you!) source: https://stackoverflow.com/questions/26594153/angularjs-directive-in-ng-bind-html
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
Jetris.php:
<?php //Jon Crawford AkadineWebOS pages/jetris.php
//describe window
$window = array();
$window['content'] = "<canvas id='gamewindow' width='780px' height='560px'></canvas>";
$window['title'] = "Jetris";
$window['x'] = 20;
$window['y'] = 250;
$window['id'] = 900;
$window['requireJS'] = array();
$window['requireJS'][0] = "scripts/html5Jetris.js";
$window['requireJS'][1] = "scripts/html5Canvas.js";
$window['requireJS'][2] = "scripts/utilities.js";
echo json_encode($window);
?>
This gets loaded to a div with title bar and exit button that is draggable. My menu system is icon like. My AkadineWebOS looks like a regular desktop. I have an about.php and a welcome.php that work just fine in my div-windows.
Ok so this is a special case I probably did not clarify my question. Here are the steps I used to accomplish my goal.
To load an HTML formated string to the DOM I used a $compile directive by Joël (Thank you!) source: AngularJS directive in ng-bind-html
To load external dependency script files, I used a load script function by Ben Thielker (Thank you!) source: Single page application - load js file dynamically based on partial view
Then I change the onload in my java game Jetris to: var externalJS = { run: function() { window.jetris.startGame(); } };
So that I will not have to re-code my angular app whenever I want to add a new game or other javascript app, this allows my app to check if var externalJS is undefined and if is is defined, it executes externalJS.run();. If the app's div-window is closed, this will effectively restart it when reopened.
Now since everything is loaded using Angular's HTTP module, you have to wait for Angulars promise to finish for everything to be loaded. If I try to run it to soon, I get a blank window and no game. To that end, I use
setTimeout(function() {
if (typeof externalJS != 'undefined'){
externalJS.run();
}},750);
in the HTTP module's successCallback to give it some time.
Here is a link to a working Alpha of the project, with all issues questioned here ironed out. You can drag the windows and icons, open more than one of the same window like a normal desktop unless a oneInstance flag is set like with Jetris, and you can play Jetris! Check it out! AkadineWebOS
Triva: I created the acronym Akadine as a online handle with a program that made random readable words (not real, just readable) years ago, and came up only recently with Advanced Kiosk And Dynamic Internet Navigation Environment.

array reference javascript angular

i'm trying to reference one item in an array, and i have no idea why this is not working,
console.log($scope.Times);
console.log($scope.Times[0]);
these two lines of code are EXACTLY after eachother, but the output i get from the console is the following..
Output from the console
any ideas why this is not working? the commands are exactly after each other as I mentioned before and in the same function, the variable is global in my controller.
i can add more code if you think it can help, but i don't really understand how..
some more code:
$scope.Times = [];
$scope.getStatus = function(timer){
$http.post('getStatus.php', {timer : timer})
.success(function(response){
$scope.response = response;
if ($scope.response.Running === "0"){
$scope.model = { ItemNumber : $scope.response.Part };
$scope.loadTiming($scope.response.Part);
console.log($scope.Times);
console.log($scope.Times[0]);
}
});
};
$scope.loadTiming = function(itemNumber) {
$http.post('getPartTimings.php', {itemNumber : itemNumber})
.success(function(response){
$scope.selectedTiming = response;
$scope.Times.splice(0);
var i = 0;
angular.forEach($scope.selectedTiming, function(value) {
if (value !== 0)
$scope.Times.push({
"Process" : $scope.procedures[i],
"Duration" : value*60
});
i++;
});
});
};
<?php
$postData = file_get_contents("php://input");
$request = json_decode($postData);
require "conf/config.php";
mysqli_report(MYSQLI_REPORT_STRICT);
try {
$con=mysqli_connect(DBSERVER,DBUSER,DBPASS,DBNAME);
} catch (Exception $exp) {
echo "<label style='font-weight:bold; color:red'>MySQL Server Connection Failed. </label>";
exit;
}
$query = 'SELECT *,
TIME_TO_SEC(TIMEDIFF(NOW(),Timestamp))
FROM live_Timers
WHERE Timer='.$request->timer;
$result = mysqli_query($con, $query);
$data = mysqli_fetch_assoc($result);
echo JSON_ENCODE($data);
thanks for your help.
OK, so more code does help. It looks like you have asynchronous logic happening here. loadTiming is fired, which does a POST and then a splice on the Times array. One console.log could be firing before this POST and the other after. There's no easy way to tell.
One possible fix would be to only log these once the loadTiming async process runs. Return a promise from the loadTiming function and then in the then callback of the promise, log your array.
$scope.getStatus = function(timer){
$http.post('getStatus.php', {timer : timer})
.success(function(response){
$scope.response = response;
if ($scope.response.Running === "0"){
$scope.model = { ItemNumber : $scope.response.Part };
$scope.loadTiming($scope.response.Part).then(function () {
console.log($scope.Times);
console.log($scope.Times[0]);
});
}
});
};
$scope.loadTiming = function(itemNumber) {
return $http.post('getPartTimings.php', {itemNumber : itemNumber})
.success(function(response){
$scope.selectedTiming = response;
$scope.Times.splice(0);
var i = 0;
angular.forEach($scope.selectedTiming, function(value) {
if (value !== 0)
$scope.Times.push({
"Process" : $scope.procedures[i],
"Duration" : value*60
});
i++;
});
});
};
I think your issue is a $scope reference issue.
I would try this:
$scope.vm = {};
$scope.vm.Times = [];
Adding the "." is Angular best practice when attaching to $scope. This is best described here Understanding Scopes
I have experienced a similar situation a while ago, related with this issue.
Since then, I've encountered related issues a bunch of times (AngularJS, due to its cyclic nature seems prone to produce this behaviour).
In your case, using JSON.stringify($scope.Times) might "fix" this.
Context
Usually this happens in this context:
An async call or a expensive DOM manipulation is made.
You make 2 (or more) calls to console.log in between.
The state of the DOM or object is changed
The output shows inconsistent (and strange) results
How
Take this example:
console.log(someObject);
console.log(someObject.property);
After digging a lot (and talking to Webkit developers) this is what I've found:
The second call to console.log is "resolved" first.
Why?
In your case, this has to do how Console handles objects and "expressions" in a different way:
An "expression" is resolved in the time of call, while with objects, a reference to said object is stored instead
Note that expression is used loosely here. You can observe this behaviour in this fiddle
More in depth analysis
Regarding display discrepancies, the behaviour posted above is not the only gotcha with Console. In fact, it is related in how Console works.
Console is an external tool
First you must realize that Console is an external tool and not part of the ECMAScript spec. Implementations differ between browsers and it shouldn't be used in production. It certainly won't work the same for every user.
Console is a non-standard external tool and is not on a standards track.
Console is dynamic
Console as a very dynamic tool. With console you can make assertions (test), time and profile your code, group log entries, remote connect to your server and debug Server Side Code. You can even change code itself, at runtime. So..
Console is not just a static log displayer... Its dynamic nature is one its most features
Console has a slight delay
Being an external dynamic tool, Console works as a watcher process attached to the javascript engine.
This is useful in debugging and among other things prevents Console to inadvertently block the execution of the script. A simple and crude way of thinking about this is picturing console.log as a kind of non-blocking async call. This means that:
With Console, there's a slight delay between 1)call, 2)processing and 3)output.
However, calling Console is not "instant" per se. In fact, by itself, can delay script execution. If you mix this with complex DOM manipulations and events, it can cause weird behaviours.
I've encountered an issue with Chrome, when using MutationObserver and console.log. This happened because the DOM Painting was delaying the update of the DOM object but the event triggered by that DOM change was fired nevertheless. This meant the event callback was executed and finished before the DOM Object was fully updated, resulting in an invalid reference to the DOM object.
Using console.log in the observer caused a brief delay in the callback execution, that, in most of the times, was enough to let the DOM Object update first. This proves that console.log delays code execution.
But even when an invalid reference error occurred, console.log ALWAYS showed a valid object. Since the object couldn't have been changed by code itself, this proves there is a delay delay between the call of console.log and the processing.
Console log order matches the code path
Console log entries order is unaffected by entries update status. In other words,
The order of the log entries reflect the order in which they are called, not their "freshness"
So, if an object is updated, it does not move to the end of the log. (makes sense to me)
Counterintuitive behaviour
This can lead to a number of possible counterintuitive behaviours because one might expect a console.log to be some kind of snapshot of the object, not a reference to it.
For instance, in your case, the object is changed between the the call to console.log and the end of the script.
At the time of calling, $scope.Times is empty, so $scope.Times[0] is undefined.
However, the $scope.Time object is updated posteriorly.
When the Console report is displayed, it shows an updated version of the object.
Fix
In your case, transforming the object in an "expression" can solve the "issue". For instance, you can use JSON.stringify($scope.Times).
Debate
It is debatable if the way console handles objects is a Bug or a Feature. Some propose that, when called with an object, console.log should clone that object making a kind of snapshot. Some argue that storing a reference to the object is preferable, since you can easily create a snapshot yourself if you wish to do so.

JsApi sometimes gets broken

We're using the JsApin for two way communication between the plugin and the page js.
Sometimes this object just stops working.
Here's what we're doing:
<object type="...">
...
<param name="onload" value="pluginloaded" />
...
</object>
var myObj = {
element: ..., // reference to the dom element for the object tag
...
}
function pluginloaded(jsapi) {
myObj.jsapi = jsapi;
}
As you can see we keep a reference to both the DOM object element and the JsApi object that is being passed to the onload method.
When there's a try to execute a method on the plugin (or a property) it will first try myObj.jsapi.method() and if that failed then 'myObj.element.method()`, and if that fails then that will be reported.
Statistics show that it doesn't happen very frequently, but it does indeed happen, though I have no idea how to reproduce it, just sometimes happens.
Any idea what might cause this object to be unavailable? from the js perspective the jsapi object is undefined, and the object element just doesn't have the method/properties which are exposed from the plugin.
Thanks.
Without spending more time looking at your actual project it's hard to say for sure, but this sounds to me like you're actually unloading and/or reloading your plugin. The most common thing that causes this is when you move it around in the DOM -- for example, if you use javascript to create the object tag, and you set the .type before you put it in the DOM:
var obj = Document.create("object");
obj.type = "application/x-mytype";
someElement.appendChild(obj);
This seems like a good idea, but the browser actually will partially destroy the plugin object when you do this. Similarly, if you set css display: none or overflow: {anything here} on the plugin object or any of its parents it can cause this.
Anyway, however it is happening I bet you are unloading the plugin, thus invalidating the jsapi object you grabbed.

Weird Javascript IOS 7 Issue

I have a web application which behaves fine on all desktop browsers and mobile devices however since IOS 7 I am encountering a weird issue.
Example:
I am setting an object like
(function(){
//CONSTRUCTOR*
sampleObject= new function(){
alert("loaded constructor "+ new Date().getTime()); //only outputted once
//swfobject dom load event
}
...//functions and propeties related to sampleObject
})();
//ASSIGNING THE OBJECT
sampleObject = new sampleObject();
alert("finished assigning object "+ new Date().getTime()); //only outputted once
Issue:
On ios 7 it seems to randomly lose what sampleObject is and instead reverts to what is inside the snippet with constructor*. So for example, I have been debugging it at intervals where I am calling a public method on my object such as sampleObject.getResource("a");
And the traces are coming back like:
sampleObject = [Object] //Correct
sampleObject = [Object]
and then randomly it does this:
sampleObject = function(){
//swfobject dom load event
}
Which basically is the code inside the constructor*.
This then causes my code to throw a reference error as the public properties/methods which I am using throughout such as getResource are not defined...
Things Tried:
Code only initiates once (alerts fire once with single Date / random string)
I Identified where an error was occuring due to this issue, wrapped around a try/catch however although the code is clearly failing it is not going into the catch.
Tried it on different IOS versions, all fine except IOS 7
*CONSTRUCTOR - not sure if this is classified as a constructor but that is what it seems to me.
**Unfortunately I cannot post my code publicly as it is part of a commercial project and quite extensive.... However any suggestions or has anyone had any similar issues?
Thanks!
Thanks for all the comments above. That pointed me in the right direction.
The problem seems to be that IOS 7 seems to randomly loses scope of the javascript object, and because we werent specifically assigning the sampleObject to the window then it wasnt working all the time.
Therefore solution to my problem was simply to change:
//ASSIGNING THE OBJECT
sampleObject = new sampleObject();
to
//ASSIGNING THE OBJECT
window.sampleObject = new sampleObject();

Issue with retrieving object data on IE 8 on Windows XP or 2003

This is an interesting problem that I am facing with JavaScript and IE8 on Windows XP and Windows 2003. I create an object on the page and then retrive information about that object (for example, its version). When trying to get the version, I am running this code:
var myObject = document.getElementById(objectId);
console.log(myObject.version);
What is interesting is that this code works on every single browser except IE8 on Windows XP and 2003. I've done some debugging and this is where things get interesting.
myObject is not null but myObject.version is undefined. So what I did is I added an alert in between so the code is now as follows:
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
The alert results in "undefined", however, the console.log is now resulting in the actual version. If I add an alert before this alert of anything (let's say alert("something")) then the second alert has the actual version now. I am assuming this is a timing issue (for some reason the object needs sometime to be able to provide the data stored in it?) but I am not sure what kind of timing issue this is or how to approach it.
Sorry for the long description but any help is appreciated.
document.getElementById doesn't return an object. It returns a DOM element. So, you expect to see a .version property in a DOM element, which by the official W3C specification is missing (or at least I don't know about this).
I'm not sure what you are expecting to see in .version, but if it is something custom then you should create a custom object like that:
var o = { version: "..." }
console.log(o);
You said that this may be a time issue. If that's true then I'll suggest to try to access the .version property after the DOM is fully loaded. You can use jQuery for the purpose:
$(document).ready(function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
});
You can add a setTimeout in your function till the .version property is there.
var f = function(callback) {
var check = function() {
var myObject = document.getElementById(objectId);
alert(myObject.version);
console.log(myObject.version);
if(typeof myObject.version !== "undefined") {
callback(myObject.version);
} else {
setTimeout(check, 1000);
}
}
setTimeout(check, 1000);
}
What happens if you put the <script>...</script> tag with the js code at the end of the html file? In my opinion, the code is executed when the DOM is not ready. If you put it in the end, then it will be executed after it's loaded.

Categories

Resources