The leak is pretty easy to create. Place the HTML below alongside a list of large images named "TestImage0.jpg", "TestImage1.jpg",..."TestImage9.jpg". The page will leak memory (I used sIEve for testing) on every click of the page. If the resize css is removed, the page will not leak. Can anyone confirm that this is an IE8 problem, or that my experiment is flawed?
Test Code
<html>
<head>
<title>Memory Leak Testing</title>
<script type="text/javascript">
var count = 0;
window.onload =
function() {
AppendImage();
window.document.body.onclick = ReplaceImage;
}
var ReplaceImage = function() {
window.document.body.removeChild(document.getElementById('MemTestObject' + count));
count++;
if (count > 9) {
alert('No more images to load.');
} else {
AppendImage();
}
}
var AppendImage = function() {
var imageObject = document.createElement('img');
imageObject.id = 'MemTestObject' + count;
imageObject.className = 'MemTestObject';
imageObject.src = 'TestImage' + count + '.jpg';
window.document.body.appendChild(imageObject);
}
</script>
<style type="text/css">
.MemTestObject {
width: 140px;
height: 178px;
}
</style>
</head>
<body>
<h1>Memory Leak Testing</h1>
</body>
</html>
The question is "Can someone verify that this is an IE8 memory leak?" To which the answer can only be, yes some one could verify this.
If you really think it is a valid memory leak in IE, first make sure that it is just IE. Then carry on working out specifics. Once you can describe exactly how to recreate this leak, you can consider reporting to MS, but they probably will not bother fixing it any time soon. But by all means spread the news so that others can avoid the trap
Related
i new start a video in a specific position when the web is loading, i get the position using a eloquent var coming from the controler.
I also use jquery for other functions in the same view.
I have checked the variables arrive correctly so I do not know where is the error.
The strange thing is this method only works if I show a alert popup before in firefox. (not work in chrome or other browser).
This is my code:
<video id="video" style="display:none; width:100%; height:100%;" autoplay>
<source src="/files/convert/videos/{{$moviesNow->url}}" type="video/mp4" />
Su navegador no soporta el tag video.
</video>
<script>
var vid = document.getElementById("video");
var time = {{$difTime}};
var isPlaying = {{$playNow}};
var moviesArr = [];
};
var j = jQuery.noConflict();
j(document).ready(function() {
if(time >= 0 && isPlaying == 1){
//vid.currentTime = time;
}
});
function setCurTime() {
vid.currentTime = time;
};
</script>
Also i trying use:
document.getElementById('video').addEventListener('loadedmetadata', function() {
this.currentTime = time;
}, false);
But the problem is not solved.
The loadedmetadata event is helpful, but it doesn't ensure that the section of video you are trying to access is seekable yet. You could try a simpler solution using Media Fragments:
Just add #t=120 to the end of the video url to have it auto-forward to that spot. Like this:
video {
height: 180px;
width: 320px;
}
<video src="http://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4#t=120" controls autoplay></video>
In my JQM 1.4 + Phonegap 3.6 app, I am using a listview as in the following code.
HTML:
<div id="boardselection">
<ul id="modelsListview" data-role="listview" data-icon="false">
</ul>
</div>
JS:
function resetModelsListView(prodata, firsttime, funfeatureOn, specificBrand, specificPro) {
console.log("on passe dans resetModelsListView");
// funfeatureOn = 0;
//debug timer
var time = [];
var dummy;
var i;
var listviewdeferred = $.Deferred();
var optionspro = '';
var optionsbrand = '';
var optionsmodel = '';
var countpros = 0;
var countbrands = 0;
var countmodels = 0;
var chosenmodelListViewHandle = $('#modelsListview');
var chosenbrandSelect = $('#chosenbrand');
optionsmodel += '';
var alreadyusedbrands = [];
prodata.sort(SortByName);
// get previously selected model to reselect it later
//var previouslySelectedModelId =parseInt(chosenmodelSelect.find('li:selected').val());
if (!funfeatureOn) {
prodata.sort(SortByModel);
} else {
prodata.sort(SortByFUN);
}
//populate model list
//~ if (firsttime){
//~ var perfIsChecked = true;
//~ var smallwaveIsChecked = true;
//~ var stepupIsChecked = true;
//~ }else {
var perfIsChecked = $('#checkboxperf').is(":checked");
var smallwaveIsChecked = $('#checkboxsmallwave').is(":checked");
var stepupIsChecked = $('#checkboxstepup').is(":checked");
//~ }
console.log("perfIsChecked, smallwaveIsChecked, stepupIsChecked =");
console.log(perfIsChecked);
console.log(smallwaveIsChecked);
console.log(stepupIsChecked);
//if none checked then no filter
if (!perfIsChecked && !smallwaveIsChecked && !stepupIsChecked) {
perfIsChecked = true;
smallwaveIsChecked = true;
stepupIsChecked = true;
}
for (i = 1; i < prodata.length; ++i) {
if (specificBrand && prodata[i]['brand'] != specificBrand) {
} else if (specificPro && prodata[i]['name'] != specificPro) {
} else {
if (prodata[i]['fun'] == 0 && perfIsChecked) {
optionsmodel += '<li><a class="optionfuninit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
} else if (prodata[i]['fun'] == 1 && smallwaveIsChecked) {
optionsmodel += '<li><a class="optionfuninit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
} else if (prodata[i]['fun'] == 2 && stepupIsChecked) {
optionsmodel += '<li><a class="optionstepupinit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
} else if (prodata[i]['fun'] == 3 && smallwaveIsChecked) {
optionsmodel += '<li><a class="optionkidsinit" href="#" data-proid="' + prodata[i]['id'] + '"><div class="listviewtexts"><span class="listviewtextsmodel">' + prodata[i]['model'] + '</span> - <span class="listviewtextspro">as surfed by ' + prodata[i]['name'] + '</span></div></a></li>';
}
if (prodata[i]['model'] !== prodata[i - 1]['model']) { //eliminate name duplicates if prodata sorted by model
countmodels = countmodels + 1;
}
}
}
chosenmodelListViewHandle.html(optionsmodel);
if (chosenmodelListViewHandle.listview("option", "disabled")) {
chosenmodelListViewHandle.listview("option", "disabled", false);
}
//~ if (resetModelsOnly) {
//~ if ( !isNaN(previouslySelectedModelId) ) {
//~ chosenmodelListViewHandle.find('li[href="' + previouslySelectedModelId + '"]').attr("selected", "selected").siblings('li').removeAttr('selected');
//~ }
//~ }
//~ highlightFunModels(funfeatureOn, 1);
//~ highlightStepupModels(funfeatureOn, 0);
chosenmodelListViewHandle.listview("refresh", true);
$("#chosenmodel-button").addClass("ui-icon-carat-d ui-btn-icon-right");
if (!funfeatureOn) {
} else {
$('ul#chosenmodel-menu').find("a.ui-btn:contains(SMALL-WAVE)").addClass("optionfun");
$('ul#chosenmodel-menu').find("a.ui-btn:contains(STEP-UP)").addClass("optionstepup");
}
prodata.sort(SortById); //we need this otherwise prodata is not usable by the $('#chosenpro').trigger
$("#chosenmodel-button span").attr({ 'data-i18n': 'select.3' });
$("#boardselection").i18n();
listviewdeferred.resolve();
return listviewdeferred;
}
This dynamically displays a long list of images and text, depending on wether filters (checkboxes) are checked or not, and this list is quite long to generate and particularly difficult to scroll in iOS.... The performance is bad.
Can you help me figure out a way to improve the performance.
The accepted answer is not correct. Your code is slow, but not because of jQuery Mobile. Take a look at the loop in your jsFiddle: jsfiddle.net/L3gr46s8/4
for (i = 0; i <= 50; i++) {
$('ul[data-role="listview"]').append('<li>' + 'list item ' + i + '</li>');
$('ul[data-role="listview"]').listview('refresh');
}
There are a couple very significant problems in those four lines of code.
First, your code executes two DOM traversals to locate the ul on the page. You could execute $('ul[data-role="listview"]') before the loop and store the result in a variable:
var listView = $('ul[data-role="listview"]');
Second, your code inserts the list item directly to the DOM and you instruct jQuery Mobile to immediately apply markup enhancement with .listview('refresh');. This is extraordinarily expensive! Especially on under-powered mobile devices. Depending upon the browser and page layout, those two lines could trigger a full page re-draw with every iteration of the loop.
You should render the content as a DocumentFragment (in memory), insert it all to the DOM in one action and tell JQM to enhance the markup once at the end. Even simply moving $('ul[data-role="listview"]').listview('refresh'); out of the loop would be a dramatic improvement.
Here is some additional reading on the importance of rendering your content first in memory before inserting it into the DOM:
How expensive is it to dynamically insert DIVs using JavaScript?
John Resig - DOM DocumentFragments
To put it short, jQuery Mobile is simply slow.
I have a dynamic listview in my app and also had performance issues when using jQuery Mobile. I came to a conclusion that the issue was in rendering and was caused by jQuery Mobile. I implemented my own styling and the rendering time came down from 170ms to 25ms.
Here's some backup to my point (3 articles): http://apachecordova.blogspot.fi/search/label/jQuery%20Mobile
EDIT:
As an answer to your question in the comments, I don't think it would help if I posted my code here. The whole point is that you only write the code YOU need. My listview probably is totally different than yours.
To prove my point (again), I made two listviews. The first one is a basic jQM listview. The other one is styled with custom CSS and it's pretty close to what I use in my app. There's a button in both which renders the listview. What is happening under the hood is very different:
jQM: As you can see, there's a lot of stuff (that you may not need) going on
Custom CSS: attached an event listener to all the elements to make the comparison more fair
These profiles have been recorded with Chrome Developer Tools and the difference is obvious: 173ms vs 12ms. This custom CSS took me about 5min to write:
#custom-listview {
list-style-type: none;
padding: 0px;
margin: 0px;
}
#custom-listview li {
display: block;
position: relative;
overflow: visible;
}
#custom-listview a {
display: block;
position: relative;
text-overflow: ellipsis;
text-decoration: none;
color: white;
padding: .7em 1em;
font-size: 16px;
background-color: #333;
border: solid 1px #1f1f1f;
overflow: hidden;
white-space: nowrap;
font-family: sans-serif;
}
I had to add some code here, because SO won't let me link to Fiddle without:
jQM
Custom CSS
I'm not saying jQuery Mobile is all bad. It's good for many things. But if you have complex structure and/or a lot of data, the performance may become an issue especially in PhoneGap apps. That's the conclusion I have come to with my little experience.
Good day ! I'm learning javascript and have a problem with the timers. I just wanted to check if the onload event is triggered once the whole page is written including the text modified via javascipt. For that purpose I wanted to slow down the writing of the text by inducing a 200 ms delay between characters.
The test I use is the following:
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>Onload test</title>
<script>
function load() {
alert("The page is considered loaded !");
}
function writeSlowly(text, timer) {
var L= text.length;
var st = "";
function write (seq) {
document.getElementById("st").innerHTML = seq;
};
for (var i = 0; i < L; i += 1) {
st += text[i];
setTimeout(write(st), timer);
};
};
</script>
</head>
<body>
<p id="st"></p>
<script>
writeSlowly("This pages takes a while to load", 200);
window.onload = load;
</script>
</body>
</html>
The page loads as if there were no delay at all. Actually I expected the text (32 characters long) to take about 32 x 200 ms =~ 7 seconds. When debugging (with Firebug - I use Firefox 30) the program steps through the lines but the timer has no effect. The page displays almost instantaneously.
You are creating separate timers for each letter, all start at time 0 and all are executing at time 200ms.
Further, the function for setTimeout needs to be a callback (the function will be called back into when the timer expires). You are passing it a null. write() does not return anything much less a function.
So you are actually writing each letter every time you hit the loop, resulting in no delay
To achieve what you are trying I would do something along the lines of...
var str;
var index = 0;
function writeSlowly(text, timer) {
str = text;
setInterval(writeNext, timer);
};
function writeNext()
{
if(index < str.length - 1)
document.getElementById("st").innerHTML = str.substring(0, ++index);
else
document.getElementById("st").innerHTML = str;
}
I made a few modifications and made it work, you can try it out at this link.
One issue is that you were calling the write function, not setting it as a callback. Another issue is that the string you wanted to write was getting filled up completely before you wrote it. Finally, the timer was being set at 200ms from the current time for all writes, instead of introducing a delay of 200ms for each character written.
The updated Javascript is below.
function writeSlowly(text, timer) {
var L= text.length;
var st = "";
var delay = 0;
for (var i = 0; i < L; i += 1) {
st += text[i];
delay += timer
setTimeout(writer(st), delay);
};
}
function writer(toWrite) {
return function() {
document.getElementById("st").innerHTML = toWrite;
}
}
Edit:
I updated the JSFiddle.
When the text is done scrolling, it it will trigger the done() function and run whatever code you'd like to run at that point.
Following the answers given, their analysis (see comments below the original question) my preferred answer is the following - which like the one by Adam - takes almost exactly 5760 msec for a 2880 characters string with a 2 msec delay per character. The central part is below and the full answer on JS Fiddle.
function writeSlowly(text, timer) {
var L= text.length;
var delay = 0;
for (var i = 0; i < L; i += 1) {
setTimeout(writer, delay += timer);
};
function writer() {
if (!writer.seq) writer.seq = 0; // Create a function property that increments on each call.
document.getElementById("slowpara").innerHTML = text.substring(0, ++writer.seq);
}
}
I thank StackOverflow, the community, in particular Cheruvian and Adam for their generous help.
Aloha Stockoverflow.
In advance, thank you!
I am trying to modify my randomly changing background on my webpage, to add a FADE effect, so the change from 1 background to another is not so sudden and sharp.
I have tried to search through the web endlessly for a solution to my issue, but it all points towards adding a jQuery plugin which I would preferably avoid if it is possible.
My working code is as follows and needs to have added some kind of fadein / fadeout effect.
<script type="text/javascript">
var num;
var temp=0;
var speed=5000; /* this is set for 5 seconds, edit value to suit requirements */
var preloads=[];
/* add any number of images here */
preload(
'images/bg1.jpg',
'images/bg2.jpg',
'images/bg3.jpg',
'images/bg4.jpg',
'images/bg5.jpg'
);
function preload(){
for(var c=0;c<arguments.length;c++) {
preloads[preloads.length]=new Image();
preloads[preloads.length-1].src=arguments[c];
}
}
function rotateImages() {
num=Math.floor(Math.random()*preloads.length);
if(num==temp){
rotateImages();
}
else {
document.body.style.backgroundImage='url('+preloads[num].src+')';
temp=num;
setTimeout(function(){rotateImages()},speed);
}
}
if(window.addEventListener){
window.addEventListener('load',rotateImages,false);
}
else {
if(window.attachEvent){
window.attachEvent('onload',rotateImages);
}
}
</script>
Thank you very much for taking the time to look at it. :)
How to do it without plugins:
Use 2 layers for the background image, position them on top of each other.
Init the page with the first image on the bottom layer, make the top layer invisible (using CSS opacity property, make sure to Google this, different browsers use different approaches).
When fading:
Set the new image for the top layer.
Use a short, looping (frameduration < 40ms) setTimeout to increment the opacity of your top layer to 1. Use increments of 1/(speed/frameduration).
When comletely faded in, set the bottom layer to use the new (now visible) image, and set the top layer to opacity 0.
Like this:
<html>
<head>
<script type="text/javascript">
var num;
var current=0;
var speed=5000; /* this is set for 5 seconds, edit value to suit requirements */
var fps = 25;
var fadeDuration = 1000;
var opacityIncrement = 1/(fadeDuration/(1000/fps));
var preloads=[];
var topLayerOpacity = 0;
var topLayer = document.createElement("div");
var bottomLayer = document.createElement("div");
setOpacity(topLayer, 0);
/* add any number of images here */
preload(
'images/bg1.jpg',
'images/bg2.jpg',
'images/bg3.jpg',
'images/bg4.jpg'
);
function loadComplete(){
//add layers to background div
document.getElementById('backgroundContainer').appendChild(bottomLayer);
document.getElementById('backgroundContainer').appendChild(topLayer);
rotateImages();
}
function preload(){
//preload images
for(var c=0;c<arguments.length;c++) {
preloads[preloads.length]=new Image();
preloads[preloads.length-1].src=arguments[c];
}
}
// selecte new random image from preloads and start fade-in
function rotateImages() {
num=Math.floor(Math.random()*preloads.length);
//don't select current image
if(num==current){
rotateImages();
}
else {
topLayer.style.backgroundImage = 'url('+preloads[num].src+')';
current=num;
//start fade-in
fadeIn();
setTimeout(function(){rotateImages()},speed);
}
}
// fade in topLayer
function fadeIn(){
if (topLayerOpacity < 1){
topLayerOpacity += opacityIncrement;
setOpacity(topLayer, topLayerOpacity);// opacityIncrement);
setTimeout(fadeIn, 1000/fps);
}else{
fadeInComplete();
}
}
//return opacity for element
function getOpacity(el){
alert (el.style.opacity);
return el.style.opacity;
}
//sets opacity on element
function setOpacity(el, val){
el.style.opacity = val;
el.style.filter = 'alpha(opacity=' + val*100 + ')';
}
//called when fadeIn completed
function fadeInComplete(){
bottomLayer.style.backgroundImage = topLayer.style.backgroundImage;
topLayerOpacity = 0;
setOpacity(topLayer, topLayerOpacity);
}
if(window.addEventListener){
window.addEventListener('load',loadComplete,false);
}
else {
if(window.attachEvent){
window.attachEvent('onload',loadComplete);
}
}
</script>
<style type="text/css">
#backgroundContainer{
width:100%;
height:100%;
position:absolute;
/*background-color:green;*/
}
#backgroundContainer div{
width:100%;
height:100%;
position:absolute;
top:0;
}
.page {
width:100%;
text-align:center;
position:absolute;
}
.contents{
width:400px;
margin:0 auto;
background-color:lightblue;
}
</style>
</head>
<body>
<!-- holds background layers -->
<div id="backgroundContainer"></div>
<!-- substitutes for 'body' on this webpage -->
<div class="page">
<!-- contents for your webpage, through css centered within page-div -->
<div class="contents">
<p>Contents</p>
</div>
</div>
</body>
</html>
OR
Use jQuery/mootools/script.aculo.us/...
Best of luck!
For some reason, this code works all chrome, safari, and ei but not on firefox.
<script type="text/javascript">
function toscheck(){
if(tos.scrollTop+540 > tos.scrollHeight){
alert(tos.scrollTop + " " + tos.scrollHeight);
}
}
</script>
----------
<textarea name="tos" id="tos" readonly="readonly" onmousemove="toscheck()">text</textarea>
Nothing's triggered on FF but works fine on all other browsers.
However, <textarea.. onmousemove=alert('test')>text</textarea> works fine.
I'm new to javascript so any help would be greatly appreciated.
That should actually not work in any browser. There is a closing bracket missing in your Javascript code:
<script type="text/javascript">
function toscheck() {
if (tos.scrollTop + 540 > tos.scrollHeight) {
alert(tos.scrollTop + " " + tos.scrollHeight);
}
}
</script>
Live example
Other than your missing } to close out your function it seems to work in FF for me.
<script type="text/javascript">
function toscheck(){
if(tos.scrollTop+540 > tos.scrollHeight){
alert(tos.scrollTop + " " + tos.scrollHeight);
}
} // <----- was missing
</script>
Also, in your function you directly go to tos.property.
You'll either need to pass this into the mousemove="toscheck(this)" and have your function setup like this:
<script type="text/javascript">
function toscheck(elem){
if(elem.scrollTop+540 > elem.scrollHeight){
alert(elem.scrollTop + " " + elem.scrollHeight);
}
}
</script>
Or get your textarea from within the function like this:
<script type="text/javascript">
function toscheck(){
var tos = document.getElementById('tos');
if(tos.scrollTop+540 > tos.scrollHeight){
alert(tos.scrollTop + " " + tos.scrollHeight);
}
}
</script>
Best solution, use jQuery and it's automatically cross-browser method or see this page regarding making that code cross-browser compatible due to differences between implementation in re:
Scrolling offset - how much the page has scrolled.
var x,y;
if (self.pageYOffset) // all except Explorer
{
x = self.pageXOffset;
y = self.pageYOffset;
}
else if (document.documentElement && document.documentElement.scrollTop)
// Explorer 6 Strict
{
x = document.documentElement.scrollLeft;
y = document.documentElement.scrollTop;
}
else if (document.body) // all other Explorers
{
x = document.body.scrollLeft;
y = document.body.scrollTop;
}
and
var x,y;
var test1 = document.body.scrollHeight;
var test2 = document.body.offsetHeight
if (test1 > test2) // all but Explorer Mac
{
x = document.body.scrollWidth;
y = document.body.scrollHeight;
}
else // Explorer Mac;
//would also work in Explorer 6 Strict, Mozilla and Safari
{
x = document.body.offsetWidth;
y = document.body.offsetHeight;
}
How big is your actual text box? Maybe it's rendered in a different size than you expect and/or the scrollheight is a little bit different? Maybe it's a font size/resolution/DPI setting issue. To try to solve it, add a small overlapping amount so you actually don't have to scroll to the absolute bottom (won't work in IE for example when using keyboard navigation and Ctrl + End).
if(tos.scrollTop + 565 > tos.scrollHeight){
On a side note, I wouldn't check this in onmousemove as people might use keyboard keys to navigate as well. I'd suggest using onblur and maybe onmouseup and/or onkeyup. onmouseup however might not fire if the mouse button is released while being on the scroll bar.