{"id":38869,"date":"2025-10-18T15:28:57","date_gmt":"2025-10-18T22:28:57","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=38869"},"modified":"2025-10-18T15:28:58","modified_gmt":"2025-10-18T22:28:58","slug":"how-to-check-if-an-image-has-loaded-in-javascript","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/","title":{"rendered":"How to check if an image has loaded in JavaScript?"},"content":{"rendered":"\n<p>Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until you hit race conditions, browser caching, or background images.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Question:<\/h2>\n\n\n\n<p><em>I need to show a loader until my images are fully ready on the page. What is the most reliable way to detect image load success and failure in the browser? Specifically, how to check if an image has loaded in JavaScript? I want something that works both for existing tags and for programmatically created images, plus a safe approach for CSS background images. Any best practices for cached images or lazy loading would be appreciated.<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Answer:<\/h2>\n\n\n\n<p>The short version: listen for the <code>load<\/code> and <code>error<\/code> events, use the <code>complete<\/code> and <code>naturalWidth<\/code> properties for already-cached images, and consider <code>HTMLImageElement.decode()<\/code> when you care about paint timing.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1) Existing <code>&lt;img><\/code> in the DOM<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> img = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'img.hero'<\/span>);\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">onReady<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\u00a0 <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Image loaded and has dimensions:'<\/span>, img.naturalWidth, img.naturalHeight);\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">onFail<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n\u00a0 <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">'Image failed to load:'<\/span>, img.src);\n}\n\n<span class=\"hljs-comment\">\/\/ If the image may already be cached<\/span>\n<span class=\"hljs-keyword\">if<\/span> (img.complete &amp;&amp; img.naturalWidth !== <span class=\"hljs-number\">0<\/span>) {\n\u00a0 onReady();\n} <span class=\"hljs-keyword\">else<\/span> {\n\u00a0 img.addEventListener(<span class=\"hljs-string\">'load'<\/span>, onReady, { <span class=\"hljs-attr\">once<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\u00a0 img.addEventListener(<span class=\"hljs-string\">'error'<\/span>, onFail, { <span class=\"hljs-attr\">once<\/span>: <span class=\"hljs-literal\">true<\/span> });\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<ul class=\"wp-block-list\">\n<li><code>img.complete<\/code> is true once the fetch has finished.<\/li>\n\n\n\n<li><code>img.naturalWidth<\/code> lets you differentiate between a cached error and a real image.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2) Programmatically loading an image URL<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">loadImage<\/span>(<span class=\"hljs-params\">url, { timeout = <span class=\"hljs-number\">10000<\/span> } = {}<\/span>) <\/span>{\n\u00a0 <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">resolve, reject<\/span>) =&gt;<\/span> {\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> img = <span class=\"hljs-keyword\">new<\/span> Image();\n\u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> t = setTimeout(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\u00a0 \u00a0 \u00a0 img.src = <span class=\"hljs-string\">''<\/span>; <span class=\"hljs-comment\">\/\/ Stop downloading<\/span>\n\u00a0 \u00a0 \u00a0 reject(<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Image load timed out'<\/span>));\n\u00a0 \u00a0 }, timeout);\n\n\u00a0 \u00a0 img.onload = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\u00a0 \u00a0 \u00a0 clearTimeout(t);\n\u00a0 \u00a0 \u00a0 resolve(img);\n\u00a0 \u00a0 };\n\u00a0 \u00a0 img.onerror = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\u00a0 \u00a0 \u00a0 clearTimeout(t);\n\u00a0 \u00a0 \u00a0 reject(<span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Image failed to load'<\/span>));\n\u00a0 \u00a0 };\n\u00a0 \u00a0 img.src = url;\n\u00a0 });\n}\n\n<span class=\"hljs-comment\">\/\/ Usage<\/span>\nloadImage(<span class=\"hljs-string\">'\/images\/hero@2x.jpg'<\/span>)\n\u00a0 .then(<span class=\"hljs-function\"><span class=\"hljs-params\">img<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">document<\/span>.body.appendChild(img))\n\u00a0 .catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">3) Ensuring it is decoded and ready to paint<\/h3>\n\n\n\n<p>If an image is already cached, the <code>load<\/code> event can fire before the browser decodes it for painting. Use <code>img.decode()<\/code> for an extra guarantee.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">whenPaintReady<\/span>(<span class=\"hljs-params\">img<\/span>) <\/span>{\n\u00a0 <span class=\"hljs-keyword\">if<\/span> (img.complete &amp;&amp; img.naturalWidth !== <span class=\"hljs-number\">0<\/span>) {\n\u00a0 \u00a0 <span class=\"hljs-keyword\">try<\/span> { <span class=\"hljs-keyword\">await<\/span> img.decode(); } <span class=\"hljs-keyword\">catch<\/span> { <span class=\"hljs-comment\">\/* decode may reject on some errors *\/<\/span> }\n\u00a0 \u00a0 <span class=\"hljs-keyword\">return<\/span> img;\n\u00a0 }\n\u00a0 <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\">(<span class=\"hljs-params\">res, rej<\/span>) =&gt;<\/span> {\n\u00a0 \u00a0 img.addEventListener(<span class=\"hljs-string\">'load'<\/span>, res, { <span class=\"hljs-attr\">once<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\u00a0 \u00a0 img.addEventListener(<span class=\"hljs-string\">'error'<\/span>, rej, { <span class=\"hljs-attr\">once<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\u00a0 });\n\u00a0 <span class=\"hljs-keyword\">try<\/span> { <span class=\"hljs-keyword\">await<\/span> img.decode(); } <span class=\"hljs-keyword\">catch<\/span> {}\n\u00a0 <span class=\"hljs-keyword\">return<\/span> img;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">4) CSS background images<\/h3>\n\n\n\n<p>Background images do not emit load events. Preload with an <code>Image()<\/code> object, then apply the background once ready.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">preloadBackground<\/span>(<span class=\"hljs-params\">url<\/span>) <\/span>{\n\u00a0 <span class=\"hljs-keyword\">return<\/span> loadImage(url).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> url);\n}\n\npreloadBackground(<span class=\"hljs-string\">'\/images\/bg.jpg'<\/span>)\n\u00a0 .then(<span class=\"hljs-function\"><span class=\"hljs-params\">url<\/span> =&gt;<\/span> {\n\u00a0 \u00a0 <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'.hero'<\/span>).style.backgroundImage = <span class=\"hljs-string\">`url(\"<span class=\"hljs-subst\">${url}<\/span>\")`<\/span>;\n\u00a0 })\n\u00a0 .catch(<span class=\"hljs-built_in\">console<\/span>.error);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">5) Lazy loading and viewport observers<\/h3>\n\n\n\n<p>Combine IntersectionObserver with the patterns above to defer loading until the image is visible.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">const<\/span> io = <span class=\"hljs-keyword\">new<\/span> IntersectionObserver(<span class=\"hljs-function\"><span class=\"hljs-params\">entries<\/span> =&gt;<\/span> {\n\u00a0 <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> e <span class=\"hljs-keyword\">of<\/span> entries) {\n\u00a0 \u00a0 <span class=\"hljs-keyword\">if<\/span> (e.isIntersecting) {\n\u00a0 \u00a0 \u00a0 <span class=\"hljs-keyword\">const<\/span> img = e.target;\n\u00a0 \u00a0 \u00a0 img.src = img.dataset.src;\n\u00a0 \u00a0 \u00a0 whenPaintReady(img).then(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\u00a0 \u00a0 \u00a0 \u00a0 img.classList.add(<span class=\"hljs-string\">'is-ready'<\/span>);\n\u00a0 \u00a0 \u00a0 });\n\u00a0 \u00a0 \u00a0 io.unobserve(img);\n\u00a0 \u00a0 }\n\u00a0 }\n});\n\n<span class=\"hljs-built_in\">document<\/span>.querySelectorAll(<span class=\"hljs-string\">'img&#91;data-src]'<\/span>).forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">img<\/span> =&gt;<\/span> io.observe(img));<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">6) Handling caches and servers<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cached images can make <code>load<\/code> fire immediately. Check <code>complete<\/code> first.<\/li>\n\n\n\n<li>Prefer robust URLs and CDNs. See a quick explainer on the role of an <a href=\"https:\/\/cloudinary.com\/glossary\/image-url\">image URL<\/a> in delivery pipelines.<\/li>\n\n\n\n<li>Good hosting and caching policies improve reliability and time to first paint. For a broader look, read <a href=\"https:\/\/cloudinary.com\/guides\/web-performance\/understanding-image-hosting-for-websites\">understanding image hosting for websites<\/a>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Optional: Make this easier with Cloudinary<\/h3>\n\n\n\n<p>After you have the generic pattern in place, Cloudinary can help you deliver faster and safer. For example, serve optimized formats and quality automatically, which reduces load time variance:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">&lt;img\n\u00a0 <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span><\/span>=<span class=\"hljs-string\">\"hero\"<\/span>\n\u00a0 alt=<span class=\"hljs-string\">\"Sample\"<\/span>\n\u00a0 src=<span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/f_auto,q_auto,w_1200\/sample.jpg\"<\/span>\n\/&gt;\n\nYou can also bootstrap a blurred placeholder, then swap to the full image once your onload logic fires:\n\n<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n\u00a0 <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"hero\"<\/span>\n\u00a0 <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Sample\"<\/span>\n\u00a0 <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/w_24,e_blur:2000,q_auto,f_auto\/sample.jpg\"<\/span>\n\u00a0 <span class=\"hljs-attr\">data-src<\/span>=<span class=\"hljs-string\">\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/f_auto,q_auto,w_1200\/sample.jpg\"<\/span>\n\/&gt;<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Apply the lazy loading snippet above to replace <code>src<\/code> with <code>data-src<\/code> on intersection. This improves real-world performance for your <a href=\"https:\/\/cloudinary.com\/blog\/\">media assets<\/a> without complicating your code structure.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">TL;DR<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Check <code>img.complete<\/code> and <code>naturalWidth<\/code> first, then attach <code>load<\/code> and <code>error<\/code> listeners.<\/li>\n\n\n\n<li>Use <code>img.decode()<\/code> when you need paint-ready assurance.<\/li>\n\n\n\n<li>For background images, preload with <code>Image()<\/code> and then set <code>background-image<\/code>.<\/li>\n\n\n\n<li>Combine IntersectionObserver with these patterns for lazy loading and better UX.<\/li>\n\n\n\n<li>Optimize delivery with Cloudinary URLs using <code>f_auto<\/code> and <code>q_auto<\/code> to speed up perceived load.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Learn More<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/cloudinary.com\/tools\/png-to-webp\">PNG to WebP Converter<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/tools\/image-to-jpg\">Image to JPG Converter<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/guides\/digital-asset-management\/digital-asset-management\">Digital Asset Management Guide<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/tools\/image-upscale\">Image Upscale<\/a><\/li>\n<\/ul>\n\n\n\n<p>Want faster image workflows and simpler delivery at scale? <a href=\"https:\/\/cloudinary.com\/users\/register_free\">Sign up for Cloudinary<\/a> and start optimizing today.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until you hit race conditions, browser caching, or background images.&nbsp; Question: I need to show a loader until my images are fully ready on the page. What is the most [&hellip;]<\/p>\n","protected":false},"author":88,"featured_media":38870,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[423],"class_list":["post-38869","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-questions"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to check if an image has loaded in JavaScript?<\/title>\n<meta name=\"description\" content=\"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to check if an image has loaded in JavaScript?\" \/>\n<meta property=\"og:description\" content=\"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-10-18T22:28:57+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-10-18T22:28:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"damjanantevski\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\"},\"author\":{\"name\":\"damjanantevski\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/43592e43c12520a1e867d456b1e8cf7e\"},\"headline\":\"How to check if an image has loaded in JavaScript?\",\"datePublished\":\"2025-10-18T22:28:57+00:00\",\"dateModified\":\"2025-10-18T22:28:58+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\"},\"wordCount\":434,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA\",\"keywords\":[\"Questions\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\",\"name\":\"How to check if an image has loaded in JavaScript?\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA\",\"datePublished\":\"2025-10-18T22:28:57+00:00\",\"dateModified\":\"2025-10-18T22:28:58+00:00\",\"description\":\"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to check if an image has loaded in JavaScript?\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/43592e43c12520a1e867d456b1e8cf7e\",\"name\":\"damjanantevski\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/3b40c995531fe4d510212a06c9d4fc666d2cb8efbfebc98a94191701accf4817?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/3b40c995531fe4d510212a06c9d4fc666d2cb8efbfebc98a94191701accf4817?s=96&d=mm&r=g\",\"caption\":\"damjanantevski\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to check if an image has loaded in JavaScript?","description":"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/","og_locale":"en_US","og_type":"article","og_title":"How to check if an image has loaded in JavaScript?","og_description":"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until","og_url":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/","og_site_name":"Cloudinary Blog","article_published_time":"2025-10-18T22:28:57+00:00","article_modified_time":"2025-10-18T22:28:58+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","type":"image\/jpeg"}],"author":"damjanantevski","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/"},"author":{"name":"damjanantevski","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/43592e43c12520a1e867d456b1e8cf7e"},"headline":"How to check if an image has loaded in JavaScript?","datePublished":"2025-10-18T22:28:57+00:00","dateModified":"2025-10-18T22:28:58+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/"},"wordCount":434,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","keywords":["Questions"],"inLanguage":"en-US","copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/","url":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/","name":"How to check if an image has loaded in JavaScript?","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","datePublished":"2025-10-18T22:28:57+00:00","dateModified":"2025-10-18T22:28:58+00:00","description":"Front-end apps often need to know exactly when an image is ready so you can swap placeholders, start animations, or measure layout. It sounds simple until","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/questions\/how-to-check-if-an-image-has-loaded-in-javascript\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to check if an image has loaded in JavaScript?"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/43592e43c12520a1e867d456b1e8cf7e","name":"damjanantevski","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/3b40c995531fe4d510212a06c9d4fc666d2cb8efbfebc98a94191701accf4817?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3b40c995531fe4d510212a06c9d4fc666d2cb8efbfebc98a94191701accf4817?s=96&d=mm&r=g","caption":"damjanantevski"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1760826498\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image\/how_to_check_if_an_image_has_loaded_in_javascript_featured_image.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/38869","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/88"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=38869"}],"version-history":[{"count":1,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/38869\/revisions"}],"predecessor-version":[{"id":38871,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/38869\/revisions\/38871"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/38870"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=38869"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=38869"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=38869"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}