TLDR:
define the hi-res image to be loaded in your CSS as "invisible"
#hi-res {
visibility: hidden;
}
preload image at the Head
<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
and then wait till the DOM has fully loaded to "display" the image with
$(window).load(function(){
document.getElementById('hi-res').style.visibility='visible';
});
So what you're running into is not a 'loading' issue, but a 'rendering' issue. The browser is already doing what it thinks you want it to do, that is: swapping one image for another after it's "loaded", or more accurately "found" in the DOM. The issue then is that the browser is coming across (loading) that hi-res image sooner than when it is able to quickly render it. Essentially you want to
specifically have the browser
wait to load an image at a point when it can render it quickly.
The preload
attribute should help address this in that it is essentially a request on the DOM that says: Yo, you're definitely going to need this soon, so grab it before everything else and in full. That being said, it doesn't mean it will render all that quickly once the browser is told to display the image.
So, if you really want to double tap this to ensure the alternative image does not replace the lo-res one before the browser can devote all it's resources to rendering it on screen you can simply have it explicitly hidden from view until everything else is done loading. You can do this by using CSS:
#hi-res {
visibility: hidden;
}
and then JS on DOM:
$(window).load(function(){
document.getElementById('hi-res').style.visibility='visible';
});
Preloading
Back in the day you could have used lazy loading or even just a simple JS wait script at the bottom of your page.
However, I think the best solution to your problem would be to simply preload your images using rel="preload"
as specified in the MDN Web Docs
This could done by preloading the CSS file itself:
<head>
<meta charset="utf-8">
<title>JS and CSS preload example</title>
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">
<link rel="stylesheet" href="style.css">
</head>
Source
Or more simply on the media elements themselves:
<head>
<meta charset="utf-8">
<title>Responsive preload example</title>
<link rel="preload" href="bg-image-narrow.png" as="image" media="(max-width: 600px)">
<link rel="preload" href="bg-image-wide.png" as="image" media="(min-width: 601px)">
<link rel="stylesheet" href="main.css">
</head>
Source