JavaScript causes iPad and iPhone browsers to crash while loading images

I'm currently working on constructing an image gallery in Safari that mirrors the iPad photo app. Everything is functioning smoothly, but once I exceed around 6MB of images either by adding them to the DOM or creating new Image objects, the loading of new images ceases or the browser crashes. This issue seems to be widespread, as many others have encountered the same limit, leading me to believe that my Javascript code is not to blame.

Considering that you can stream much more than a few MB in a video element or through the in-browser media player, this limitation appears unnecessary, and there should be some sort of workaround available. Perhaps by freeing up memory or utilizing another method.

In my research, I stumbled upon this UIWebView reference.

"JavaScript allocations are also capped at 10 MB. If you surpass this limit Safari will raise an exception due to excessive memory allocation for JavaScript."

This aligns closely with what I am experiencing. Is it feasible to deallocate objects in JavaScript, or does Safari/UIWebView persistently retain a running total without releasing memory? Alternatively, is there any way to load data differently so as not to consume this 10MB?

Answer №1

Exciting Update: I have discovered a simpler method to tackle this issue, tailored to your specific application needs. Instead of juggling multiple images, consider utilizing just one <img> element or Image object (possibly two for 'this' and 'next' images if animations are required) and updating properties like .src, .width, .height as needed. With this approach, you can steer clear of the 10MB cap effortlessly. For example, in a carousel scenario, start with compact placeholders to streamline implementation.


There could be a viable workaround at hand.

The essence lies in delving deep into image management and deliberately downsizing any redundant images. While traditional methods such as

document.removeChild(divMyImageContainer)
or $("myimagecontainer").empty() aim to free up memory, they fall short on Mobile Safari due to its reluctance in deallocating memory post-removal. The answer? Optimize the image directly to minimize memory usage by modifying the src attribute. A quick way is introducing a data URL. For instance:

myImage.src="/path/to/image.png"

...transforms into:

myImage.src="data:image/gif;base64,AN_ENCODED_IMAGE_DATA_STRING"

A demonstration test below showcases the efficacy. Previously, my sizable 750KB image would overwhelm the browser, halting all JS operations. However, after resetting src, I successfully loaded the same image over 170 times. A detailed breakdown of the code functioning follows.

var strImagePath = "http://path/to/your/gigantic/image.jpg";
var arrImages = [];
var imgActiveImage = null
var strNullImage = "data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7";
var intTimesViewed = 1;
var divCounter = document.createElement('h1');
document.body.appendChild(divCounter);

var shrinkImages = function() {
    var imgStoredImage;
    for (var i = arrImages.length - 1; i >= 0; i--) {
        imgStoredImage = arrImages[i];
        if (imgStoredImage !== imgActiveImage) {
            imgStoredImage.src = strNullImage;
        }
    }
};
var waitAndReload = function() {
    this.onload = null;
    setTimeout(loadNextImage,2500);
};
var loadNextImage = function() {
    var imgImage = new Image();
    imgImage.onload = waitAndReload;
    document.body.appendChild(imgImage);
    imgImage.src = strImagePath + "?" + (Math.random() * 9007199254740992);
    imgActiveImage = imgImage;
    shrinkImages()
    arrImages.push(imgImage);
    divCounter.innerHTML = intTimesViewed++;
};
loadNextImage()

This snippet was crafted for testing purposes, necessitating adaptation for individual scenarios. Comprising three segments, the core functionality centers around

imgStoredImage.src = strNullImage;

loadNextImage() initiates loading an image and triggers shrinkImages(), binding an onload event to kickstart subsequent image loading (note: the event clearing piece is pending).

waitAndReload() introduces a delay allowing time for image rendering, crucial given Mobile Safari's slower processing of large visuals.

shrinkImages() cycles through prior images (excluding active), repurposing .src to the data url.

I opted for a placeholder image within the dataurl for illustrative purposes. For practical use, opt for a transparent gif like:

data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==

Answer №2

The download limits for iPad (6.5MB) and iPhone (10MB) are determined by the number of image elements used in setting an image through its src property. Mobile Safari does not distinguish between images loaded from cache or the network, nor does it matter if the image is injected into the DOM or not.

Another solution is that Mobile Safari can load an unlimited number of images using the "background-image" CSS property.

This proof of concept involves using a pool of precachers that set the background-image properties once the images are successfully downloaded. While this method may not be optimal and does not return the used Image downloader to the pool, it conveys the overall idea :)

This approach is inspired by Rob Laplaca's original canvas workaround found at

<!DOCTYPE html>
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>iPad maximum number of images test</title> 
<script type="text/javascript">
    var precache = [
        new Image(),
        new Image(),
        new Image(),
        new Image()
    ];

    function setImage(precache, item, waiting) {
        precache.onload = function () {
            item.img.style.backgroundImage = 'url(' + item.url + ')';
            if (waiting.length > 0) {
                setImage(precache, waiting.shift(), waiting);
            }
        };
        precache.src = item.url;
    }

    window.onload = function () {
        var total = 50,
            url = 'http://www.roblaplaca.com/examples/ipadImageLoading/1500.jpg',
            queue = [],
            versionUrl,
            imageSize = 0.5,
            mb,
            img;

        for (var i = 0; i < total; i++) {
            mb = document.createElement('div');
            mb.innerHTML = ((i + 1) * imageSize) + 'mb';
            mb.style.fontSize = '2em';
            mb.style.fontWeight = 'bold';

            img = new Image();
            img.width = 1000;
            img.height = 730;
            img.style.width = '1000px';
            img.style.height = '730px';
            img.style.display = 'block';

            document.body.appendChild(mb);
            document.body.appendChild(img);


            queue.push({
                img: img,
                url: url + '?ver=' + (i + +new Date())
            });
        }

        //
        for (var p = 0; p < precache.length; p++) {
            if (queue.length > 0) {
                setImage(precache[p], queue.shift(), queue);
            }
        }
    };
</script>
</head> 
<body> 
<p>Loading (roughly half MB) images with the <strong>img tag</strong></p> 
</body> 
</html> 

Answer №3

Lately, I've had success by utilizing <div> elements in place of <img> tags and specifying the image as the background-image of the div.

Overall, it's absurd. If the user is specifically asking for additional image content, Safari should have no problem loading it.

Answer №4

I've had success following the advice of Steve Simitzis and Andrew.

My current project involves a PhoneGap-based app with 6 main sections and approximately 45 subsections. Each subsection features a jQuery cycle gallery with 2 to 7 images, all sized at 640 x 440 (totaling over 215 images). Initially, I used ajax to load page fragments, but I eventually transitioned to a one-page layout where all sections are hidden until accessed.

After navigating through around 20 galleries, I started encountering memory warnings followed by crashes. To address this issue, I converted all images into divs with the image set as a background. This allowed me to navigate through more galleries (up to 35) before experiencing a crash, especially when revisiting previously viewed galleries.

The solution that has proven effective for me involves storing the background image URL in the div's title attribute and setting all background images to a blank gif. With so many images, it was convenient to keep the URLs within the HTML for quick reference.

When a subnavigation button is clicked, I dynamically change the CSS background image for the specific gallery being displayed using the URL stored in the div's title tag. This eliminates the need for complex JavaScript functions to manage image sources.

var newUrl = $(this).attr('title');
$(this).css('background-image', 'url('+newUrl+')'); 

Upon selecting a new subnavigation button, I reset the background images of the previous gallery divs to blank gifs. This ensures that only 2-7 images are "active" at any given time, minimizing memory usage. For any additional content containing images, I apply the same "ondemand" technique of swapping titles with background images.

With these modifications, my app now runs without any crashes for extended periods. While this solution may not be the most elegant, it has effectively resolved the issues I faced.

Answer №5

While working on a rails app, I faced a challenge when lazy loading numerous mid-size photos through infinite scroll and hitting the 10Mb limit on my iPhone. Despite attempting various solutions such as loading graphics into a canvas or replacing img src with div tags, I still struggled to overcome the limitation. Eventually, the breakthrough came by substituting all img tags with div elements containing the photo as a background.

      $.ajax({
        url:"/listings/"+id+"/big",
        async:true,
        cache:true,
        success:function(data, textStatus, XMLHttpRequest) {
          // detect iOS
          if (navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i) || navigator.userAgent.match(/iPad/i)) {
            // load html into data
            data = $(data);
            // replace img w/ div w/ css bg
            data.find(".images img").each(function() { 
              var src = $(this).attr("src").replace(/\s/g,"%20");
              var div = $("<div>"); 
              div.css({width:"432px",height:"288px",background:"transparent url("+src+") no-repeat"}); 
              $(this).parent().append(div); 
              $(this).remove(); 
            }); 
            // remove graphic w/ dynamic dimensions
            data.find(".logo").remove();
          }
          // append element to the page
          page.append(data);
        }
      });

With this solution, I could now load more than 40Mb of photos on one page without any issues. However, I did encounter a peculiar problem with some of the css background graphics failing to display. This was quickly resolved by setting the div's css background property every 3 seconds using a JavaScript thread.

  setInterval(function() {
    $(".big_box .images div.img").each(function() {
      $(this).css({background:$(this).css("background")});
    });
  }, 3000);

To see this implementation in action, visit and check it out on your iPhone or iPad.

Answer №6

No solution was found for this issue despite several attempts. The following methods were tried but all failed:

  • Attempted to change the background of a DIV using

    div.style.backgroundImage = "url("+base64+")"

  • Tried changing the .src of an image using img.src = base64

  • Removed the old image and added a new one using

    removeChild( document.getElementById("img") ); document.body.appendChild( newImg )

  • Similar to the previous method but with random height on the new image

  • Experimented with removing and adding the image as an HTML5 canvas object, creating issues due to having to create a new Image();, see *

  • Initiated a new Image() object on launch, displayed the image as <canvas>, and updated it by changing container's .src and redrawing the canvas via ctx.drawImage( container, 0,0 ).

  • Likewise to the previous method but without redrawing the canvas, simply changing the Image() object's src leading to memory consumption.

An interesting observation made was that the bug persists even if the image is not displayed! For instance, running the following code every 5 seconds without loading or displaying the image eventually crashes the memory:

var newImg = new Image( 1024, 750 );
newImg.src = newString; // A long base64 string

Strangely, mere repetition of this process, without actual display or loading of the image, results in memory overload after some time!

Answer №7

While using Javascript on an iPad, I faced an issue with memory running out when we repeatedly refreshed an image every few seconds. Although it was a mistake to refresh the image so frequently, Safari ended up crashing and taking me back to the home screen. Once I adjusted the timing of the refreshes, the web app started working properly again. It appeared that the Javascript engine couldn't efficiently handle garbage collection fast enough to get rid of all the old images.

Answer №8

When facing memory issues, a simple solution is to optimize the use of thumbnails by placing them in a canvas. By creating new Image objects for each thumbnail and drawing them into the canvas, it helps manage resources efficiently. For displaying the full-size image, utilize only one Image object and ensure to draw it into the canvas as well. Instead of inserting IMG tags on the page, opt for CANVAS tags with appropriate dimensions for both the thumbnails and main display container to prevent performance issues, especially on iPad devices.

mainDisplayImage.onload = null;
mainDisplayImage.onerror = null;

...

mainDisplayImage.onload = function() { ... Draw the image on the canvas }
mainDisplayImage.onerror = function() { ... Display an error.gif on the canvas }
mainDisplayImage.src = imgsrc_string_url;

In my case, I have generated 200 thumbnails, each around 15kb in size, while the actual images weigh approximately 1 MB each.

Answer №9

Encountered similar issues when trying to render extensive lists of images on iPhones. Simply displaying 50 images in the list was enough to cause the browser to crash or sometimes even freeze the entire operating system. Strangely, the rendered images weren't being properly garbage collected, despite efforts such as pooling and recycling a few DOM elements onscreen or using images as background-image properties. Even using Data-URIs for the images still contributed to the memory issue.

The solution turned out to be surprisingly straightforward - applying position: absolute to the list items allowed them to be efficiently garbage collected and prevented hitting the memory limit. This strategy involved keeping only around 20-30 images in the DOM at any given time, dynamically creating and removing the item's DOM nodes based on scroll position ultimately resolved the problem.

Interestingly, it appears that applying webkit-transform':'scale3d() to any ancestor of the images in the DOM plays a crucial role in this issue. It seems that rendering a tall DOM structure with GPU acceleration triggers a memory leak in the webkit renderer, causing these problems to arise?

Answer №10

I am encountering a similar problem while using Chrome, working on an extension that replaces old images with new ones within the same page (specifically the popup). Even after removing the old images from the DOM, the memory they used is not released, causing excessive memory consumption on the computer. I have attempted different CSS techniques to address this issue but have not been successful. This issue becomes evident much sooner when using devices with lower memory capacity, such as an iPad.

Answer №11

After reporting a bug to jQuery regarding memory leaks, it seems like the team will need to find a solution for this issue in Mobile Safari soon.

Check out the bug report here

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can PHP effectively interpret JSON strings when sent to it?

When working with JavaScript, I am using JSON.stringify which generates the following string: {"grid":[{"section":[{"id":"wid-id-1-1"},{"id":"wid-id-1-4"}]},{"section":[{"id":"wid-id-1-5"}]},{"section":[{"id":"wid-id-1-2"},{"id":"wid-id-1-3"}]}]} I am ma ...

The resolution of deferred actions is not as successful as foreseen

In my code, I have a method that queries documentdb for a specific document and returns the results to the caller. fetch: function(query) { var fetchDeferred = q.defer(); client.queryDocuments(collectionLink, query).toArray(function(err, docs) { ...

Ionic ion-view missing title issue

I'm having trouble getting the Ionic title to display on my page: http://codepen.io/hawkphil/pen/oXqgrZ?editors=101 While my code isn't an exact match with the Ionic example, I don't want to complicate things by adding multiple layers of st ...

Is there a way to obtain HTML code within a contentEditable DIV?

When working in a contentEditable-DIV, my goal is to extract the HTML code from the starting position (0) to the end position where the user has clicked. <div id="MyEditableId" contentEditable="true"> 1. Some text 123. <span style="background-c ...

Combining PHP code within JavaScript nested inside PHP code

I am currently facing an issue with my PHP while loop. The loop is supposed to iterate through a file, extract three variables for every three lines, and then use those variables to create markers using JavaScript. However, when I try to pass the PHP varia ...

Generate a new array using a single value extracted from an existing array

I'm new to working with arrays. Before this, I used to manually code everything in HTML. My question is, can you create an array based on the values of another array? var pmmain =["aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", ...

Break down and extract elements using typedEvent in TypeScript

Within the external library, there is the following structure: export interface Event extends Log { args?: Result; } export interface TypedEvent<EventArgs extends Result> extends Event { args: EventArgs; } export type InstallationPreparedEven ...

Learn how to dynamically insert input elements into a form upon button click and effectively handle the data processing in Laravel

Currently, I am in the process of developing a straightforward event management platform (similar to Ticketmaster or Eventbrite) using Laravel 5. As part of this project, I am working on creating an event creation page where users can input their event de ...

I am attempting to incorporate an NPM package as a plugin in my Next.js application in order to prevent the occurrence of a "Module not found: Can't resolve 'child_process'" error

While I have developed nuxt apps in the past, I am new to next.js apps. In my current next.js project, I am encountering difficulties with implementing 'google-auth-library' within a component. Below is the code snippet for the troublesome compon ...

Value of the object is currently not defined

Having difficulty determining values in a manner that allows for later accessibility. When defining Search first, Search.commands[3] becomes undefined. On the other hand, defining commandList first results in commandList.commands[0] being undefined. Is t ...

Exploring Angular testing by using mock services to simulate the behavior of other services

I am familiar with how to mock a function call to a service. However, I am facing a scenario where my MainService acts as a wrapper for multiple other services. export class MainService { constructor( public service1: Service1, public service2 ...

Retrieve the data of elements that have been clicked using jQuery

I am struggling with a particular issue and would appreciate some assistance. Recently, I've been working on developing buttons that, when clicked, add data to a form for submission. An example of the code structure is as follows: <div> < ...

Issue arises when using Ionic 7 tabs with standalone Angular components

Hey, I'm new to Ionic and trying to learn it. However, all the courses available are on the old versions 5 or 6. I attempted to learn it with Angular standalone but it didn't turn out as I expected. The new way of routing wasn't working for ...

What are the steps for setting up jScroll?

As a newcomer to JS & jQuery, I appreciate your patience. I've been working on creating a dynamic <ul> list using JS, and I'm happy that it's finally coming together. Now, my next step is to incorporate infinite scrolling into my ...

Can I rely on setTimeout to guarantee the execution of a task in NodeJS?

I am working on a REST backend using ExpressJS. One of the functionalities of this backend is to allow users to upload file assets, but these files should only exist for 10 minutes. Is it secure to rely on setTimeout to automatically delete the uploaded f ...

Is Valums Ajax file Upload capable of handling the uploaded file?

Currently, I am utilizing the Valums Ajax Fileupload script found at These are the settings I have configured: function createUploader(){ var uploader = new qq.FileUploader({ element: document.getElementById('file-uploader-de ...

Dynamic HTML element

I created an input number field and I want to dynamically display the value of this field in a container without having to refresh the page. I am currently using ajax for this purpose, but unfortunately, I still need to reload the page. HTML: < ...

Unable to properly bind events onto a jQuery object

I have been attempting to attach events to jquery objects (see code snippet below), but I am facing challenges as it is not functioning properly. Can someone provide me with a suggestion or solution? Thank you! var img = thumbnail[0].appendChild(document. ...

Transforming instance of a Class into Json format for WebService

My goal is to retrieve an object of the Product class as a Json for a webservice. Despite successfully loading all the values into the object, I encounter an error when trying to return it as Json. Below is my method: <AllowAnonymous> <HttpGet&g ...

What steps can I take to resolve this error when utilizing an npm package in client-side JavaScript?

Just when I thought I was getting the hang of socket.io, I hit a roadblock on the client side. Following my instructor's advice, I installed socket.io-client package for my project. But when I tried to use it in my client-side JS code, I encountered a ...