How to prevent images from reloading when leaving "display: none" - javascript

I am writing an Ember.js web-application, designed to be the user interface of an automation system, that polls data from the LAN server every two seconds in order to have on display always the "live" process data.
This application is accessible from a wirless hotspot, to allow registered users to browse it, so potentially any device (tablets, smartphones, laptops...) could be the actual client.
On some pages, there are icons that change according to some conditions, and to implement this effect I declared several img tags, and I make the ones I dont need invisible by styling it with CSS display: none.
In HTML:
<img class="icon-active" src="/images/icon1.jpg" />
<img class="icon-inactive" src="/images/icon2.jpg" />
In Javascript, every two seconds:
var visibleElement = null;
var invisibleElement = null;
if( this.get("whatever").active == true )
{
visibleElement = this.element.getElementsByClassName("icon-active")[0];
invisibleElement = this.element.getElementsByClassName("icon-inactive")[0];
}
else
{
visibleElement = this.element.getElementsByClassName("icon-inactive")[0];
invisibleElement = this.element.getElementsByClassName("icon-active")[0];
}
visibleElement.style.display = null;
invisibleElement.style.display = "none";
Everything works fine, on laptops and tablets, but on some smarthphones, the images are loaded every time I set visibleElement.style.display = null;, it means, every two seconds, the visible icon is GETted again and again from server.
I dont want it to happen at first to reduce data traffic, that is not a problem at all, but I don't like fetching resources even if not required, second, the image reload generates an annoying flicker effect, that is really unlookable.
How can I force every client to cache images as tablets and laptops do?
----- more info -----
Thanks everyone for your support! Here you have some news:
I tried as suggested to comment-out all the javascript code that works on style.display and modify the HTML (template) as follows:
{{#if whatever.active}}
<img class="icon-active" src="/images/icon1.jpg" />
{{else}}
<img class="icon-inactive" src="/images/icon2.jpg" />
{{/if}}
and I got the same result. So I tried to roll back the HTML and leave the javascript commented, in such way I should have always all the icons visible, and surprise... they are all flickering and being requested every two seconds...
I guess the issue is due to the fact that some (maybe not up-to-date?) smartphone browsers are redrawing completely the images as the ember-views bound data gets updated. I will investigate more on which browser/version has this problem and make sure all of the testing devices use the last version of their browsers - since ember uses the latest javascript features, better cut-out old fashioned clients.
The code used to refresh data every two seconds follows, please notify if you see anything uncommon:
import Ember from 'ember';
export default Ember.Route.extend({
model() {
// record generation code here...
},
afterModel()
{
Ember.run.later(this, function()
{
this.refresh();
}
, 2000);
}
});
----- solution -----
With new up-to-date browsers is not happening, so, this behavior exists only "in the past"... For sake of completeness I should find a solution to make it work properly also on "old" browsers, but I don't have time to spend on this.
If anyone of you figures out a 360 degrees solution an answer is still appreciated.

As I see those icons are static. So my suggestion is to use css:
1) Use spans(or divs) instead if images
2) Add icon as background image
html:
<p><span>active parameter</span><span class="icon active"></span></p>
<p><span>inactive parameter</span><span class="icon inactive"></span></p>
css:
.item
width: 16px //icon dimensions
height: 16px
// you may also add display: inline same as for images and so on
.active
background-image: url('/images/icon1.jpg')
.inactive
background-image: url('/images/icon2.jpg')
Benefit: Images will be loaded once by css engine along with styles.
PS: You can get rid of inactive class if your icon is inactive by default:
html:
<p><span>active parameter</span><span class="icon active"></span></p>
<p><span>inactive parameter</span><span class="icon"></span></p>
css:
.item
width: 16px //icon dimensions
height: 16px
background-image: url('/images/icon2.jpg')
// you may also add display: inline same as for images and so on
.active
background-image: url('/images/icon1.jpg')
PPS: It's really easy to manage classes with Ember by using classNameBindings

You need to set the image display to none.
Set the display to either inline-block or block for visible and none for not visible.
Preferably use css classes, since you are doing it with javascript. It might delay the action of hiding the images while the page loads.
You can use css classes like
.active{
display:block;
}
.inactive{
display:none;
}
Use these classes to add or toggle for a specific img element.
Still there will requests to server for images because you are only hiding the images through styles.
Display MDN

Related

How to prevent partially loaded images from displaying?

If you have, let's say, 3MB image in img tag, it will take a few seconds to load. When the image is loading, browser is sort of "printing" it - it shows the top part first, then middle and then bottom. How do I prevent this from happening?
I'd rather have the image hidden and after second or two shown - when it is fully loaded.
One way would be to give them a class that gives them opacity: 0 so they don't show:
<img src="/path/to/image" class="loading">
And in CSS:
.loading {
opacity: 0;
}
In head, we override that if JavaScript is disabled (so we're not unfriendly to non-JavaScript visitors):
<noscript>
<style>
.loading {
opacity: 1;
}
</style>
</noscript>
...and then in code at the bottom of your page, find all your images and remove the class when they've loaded, and...(see comments):
(function() {
// Get an array of the images
var images = Array.prototype.slice.call(document.querySelectorAll("img.loading"));
// Hook their load and error events, even though they may have already fired
images.forEach(function(image) {
image.addEventListener("load", imageDone.bind(null, image));
image.addEventListener("error", imageDone.bind(null, image)); // Could handle errors differently
});
// Check to see if any images are already complete
checkImages();
function imageDone(img) {
img.classList.loading("remove");
images = images.filter(function(entry) { entry != img });
}
function checkImages() {
images.forEach(function(image) {
if (image.complete) {
imageDone(image);
}
});
if (images.length) {
// Check back in a second
setTimeout(checkImages, 1000);
}
}
})();
That's a belt-and-braces approach. It proactively checks to see if images have finished loading, and also reactively handles the load and error event of images. In theory, we shouldn't need the setTimeout, and you might do testing without it, but...
Notice how once an image is complete, we remove the class so it's visible.
Old school:
To avoid the partial display of an image as it renders, save your large images as progressive, rather than baseline jpgs.
a progressive jpg renders as a series of scans of increasing quality
a baseline jpg renders top to bottom (what you described as “printing”).
The progressive option is considered more user friendly than both the sudden appearance of the image or the slow top to bottom rendering you dislike. The progressive file variant can even be smaller than its baseline counterpart.
For more about this read: The Return of the Progressive JPEG.
I think everyone here gave you some good answers and I just want to add in. 3MB is fairly big for a web image. Don't use something that large for an image being used for logo or layout. That's a larger amount of pixel data that you should only stick with if you are loading something that is a nice, large scale real-life image you want to preserve the quality to (or providing a download to a high-quality graphic of something). Besides the above, if you do a Google search, you find tons of solutions for loading images. Something nice I would use for larger images is a jQuery/ajax solution.

How to prevent a background image flickering on change

I'm applying a repeated background image from a canvas to a div via javascript like this:
var img_canvas = document.createElement('canvas');
img_canvas.width = 16;
img_canvas.height = 16;
img_canvas.getContext('2d').drawImage(canvas, 0, 0, 16, 16);
var img = img_canvas.toDataURL("image/png");
document.querySelector('#div').style.backgroundImage = 'url(' + img + ')';
I have to update it quite frequently. The problem is it flickers upon change, it doesn't appear to happen in Chrome but it's really bad in Firefox and Safari. Is it possible to stop this? I didn't think it would happen since it's a dataurl and therefore doesn't need to be downloaded.
Solution:
// create a new Image object
var img_tag = new Image();
// when preload is complete, apply the image to the div
img_tag.onload = function() {
document.querySelector('#div').style.backgroundImage = 'url(' + img + ')';
}
// setting 'src' actually starts the preload
img_tag.src = img;
Try to preload the image resource to the device storage by including the image in DOM like in the following HTML-Code. Maybe the error comes up because the image resource need to be loaded which takes some time (flickering).
<img src="imageToPreload.png" style="display:none;" alt="" />
You may prefer to use sprites-images. By using sprites your application will need less HTTP-Requests to load all ressources into your page. Also add the following CSS styles if you are using css animations. It will prevent background flickering on mobile devices:
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
Preload your image like this, no need to include a <img> with display: none
<link rel="preload" href="/images/bg-min.png" as="image">
Try adding this css to your background element:
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
It should help with flickering..
You can also "force" hardware acceleration by adding this to your background element:
-webkit-transform: translate3d(0, 0, 0);
Another option is to use image instead of DIV and change only the image url.
I struggled with this for a bit, tried preloading, appending the image to the document, etc.
In the end, I resaved the JPEG without the "Progressive" option.
That fixed the rolling flicker when the img src was swapped.
In my case changing height: 1080px; (background height) to height: fit-content;
I think that preloading all the images is essential in any case. What I found is that the way the browsers behave while changing the background image dynamically is different from one another. In Firefox for example it flickers when the change is frequent however in Chrome and Safari it doesn't.
The best solution I came up with so far is drawing the image inside a child canvas that fills the space of the whole parent div.
In all cases, the images you are using must be optimized as it affects the rendering performance.
My javascript code that works now, looks like this
const pic = new Image();
const pic2 = new Image();
pic.src="../images/settings_referrals_anim.gif";
pic2.src="../images/settings_referrals_still.png";
I don't actually reference that code in the query, for example, i use
document.querySelector(".button_Settings_referrals").addEventListener("mouseover", function() {
myDiv.style.backgroundImage = "url('../images/settings_referrals_anim.gif')";
But it seems to work. If I replace the long URL with const pic for example it doesn't work, and if I include the image object declaration and location at first time in the assignment, then the flickering stops.
This does not address all of the specifics noted by the OP, but might be useful for others. Tested in Chrome 97, Firefox 96, Android 11, iOS 15.
I have a div that includes these CSS parameters...
#div_image {
background-image: url( [Path to low-res image] );
background-size: cover;
}
I have a corresponding class that looks like this...
.div_image_highres {
background-image: none !important;
}
The corresponding class has a pseudo-element defined as follows:
.div_image_highres::before {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
content: " ";
background-image: url( [Path to highres image] );
background-repeat: no-repeat;
background-position: 50% 0;
background-size: cover;
opacity: 1;
display: block;
}
I have an img element that also points to the high-res image...
<img id="img_highres_preload" src=" [Path to high-res image ] ">
The img element has a corresponding style which allows the image to be displayed (ensuring that image file loads) but not seen...
#img_highres_preload {
width: 1px;
height: 1px;
}
Two notes: (1) I realize a lot of people use other methods of pre-loading (e.g., programmatically), but I have a personal preference for this method. (2) See the addendum about the reliability of the load event.
Last but not least, I have some Javascript (jQuery) that adds the "high-res" class to "div_image" once the high-res file is loaded...
$(document).ready(function() {
$("#img_highres_preload").off().on("load", function() {
$("#div_image").addClass("div_image_highres");
});
});
This could easily be vanilla JS, but since I use jQuery throughout my code, I like having a consistency.
Here's a summary of what's happening...
Presumably, the low-res image is loaded first and becomes the background image for the div. Even if that does not occur, everything will work as intended (i.e., the high-res image will be displayed).
When the high-res image loads into the img element (i.e., Javascript confirms that the high-res file is loaded), the "div_image_highres" class is applied to "div_image".
As result, the div switches to the high-res image without flashing. In my experience, if anything, it shifts a little to the left; but that often doesn't occur and, if it does, it's not inelegant.
And here's the primary reason I use this approach when required: In my application, there are multiple panels the user can navigate, which results in one panel sliding out of view and the new one into view. If I don't use a pseudo-element (as described above) for displaying a high-res image, the image flickers when its div is hidden and re-displayed. With the above-described technique, I can slide the div in and out of view without any flickering.
Regarding the Load Event
You can't depend on the load event firing. For instance, it typically does not fire when the browser has cached an image. So to make a long post even longer, here's the enhancement I have in my code to accommodate that reality...
I modify the document.ready event (shown above) to look like this:
$(document).ready(function() {
positionOnPage(true);
$("#img_highres_preload").off().on("load", function() {
checkImage();
});
});
checkImage = function() {
var image = $("#img_highres_preload")[0];
if (!image.complete || (typeof image.naturalWidth != "undefined" && image.naturalWidth == 0)) {
console.log("Waiting for high-res image.");
}
else if (!$("#div_home").hasClass("div_home_highres")) {
$("#div_home").addClass("div_home_highres");
$("#img_highres_preload").remove();
}
}
The checkImage function examines the image element to see whether an image has in fact been loaded. In this code example, it is a little redundant — that is, the img element has confirmed the load, so there's usually no need to check it (unless there is some reason to believe the file is being misloaded).
I might do it as shown because I also call checkImage from other places in my code, so if I have more of a programmatic response (unlike the simple version shown), I want all of that code in the same place and written just once. The checkImage function might be called when triggered by a timer or when the section displaying the intended image is about to be displayed. Perhaps something like this...
if (sectionName == "[whatever]" && $("#img_highres_preload").length === 1) {
checkImage();
}
In this example, I look for the presence of the preload img element because I know that my previous function removes the element after it has fulfilled its purpose.
This post has a stripped-down version to illustrate the concept. As written above, it only accommodates a single known img element, so the code could be extended to call checkImage with some parameters (e.g., the name of an image or the element itself) and checkImage could look for the existence of the preload element, so that check occurs in one place. It can be fairly fancy, so I went with the simplest example for this post.
In many cases, this stripped-down version is all I need because typically I only use a high-res photo for a window background image. I either start with the display of a low-res image and switch it out as soon as the high-res file is loaded, or I have some animation that gets triggered after I confirm the presence of the high-res image.
A good case for a more generalized version is when I need a series of images loaded at the outset and don't want to start until all of them are ready. In those cases, the web page might begin with some welcome text that stays displayed until all images have been confirmed.
Hey Guys I know this has been an older question but if you are still flickering after all this you can simply put the final version behind you background div. That flicker is seeing behind the image you currently have so if its the final image it will be smooth.

Trying to get rid of the address bar when using ChocolateChip-UI

I've been using ChocolateChip-UI (http://www.chocolatechip-ui.com/) for a couple of days, and really like the way it manages to map the look to established mobile standards.
One problem I have with adapting my site to CC-UI has been my inability to make the address bar on scrolling. I tried everything, including meta tags, or even the hack with scrolling to 1px at onLoad. Nothing worked. As you can see, even the demo they have does not seem to make the address bar disappear.
How can I fix this? I really need those 40-50px on the top. I think that the address bar, especially on iOS older than v7, breaks the consistency of the design, and consequently lowers the attention of the user
There is a discussion about this in the CHUI Google Group. You can reach it here: https://groups.google.com/forum/#!topic/chocolatechip-ui/XOr7b8HGNK8
From Robert Biggs answer on this
$('body').addClass('hideGlobalNav');
Then have some CSS in your document's header for a custom style:
body.hideGlobalNav #global-nav {
display: none !important;
}
body.hideGlobalNav #articleWithoutGlobalNav {
top: 0 !important;
}
Then, you'd need to remove that class from the body tag when the user
navigates away. I'm not sure how the navigation is set up in your app,
whether the user leaves by going back or forward, but you can handle
that in several ways. You can add event listeners for navigation and
when the user is leaving #articleWithoutGlobalNav, then you would
remove 'hideGlobalNav' from the body tag. You could do something like
this:
$('article').on('navigationend', function(e) {
// e.target is the current article that loaded
if (e.target.id === 'articleWithoutGlobalNav') {
$('body').addClass('hideGlobalNav');
} else {
$('body').removeClass('hideGlobalNav');
}
})

Supporting non-Javascript users - JQuery Slide

I am currently using display:none to hide all the divs on my website. Users click on like for example "info" or "contact" and the appropriate div will slide down via JQuery. To support users without Javascript, the links goes to "info.php" and "contact.php" if Javascript is no enabled.
This is quite a hassle to maintain because I have to update both the main page and the non-javascript versions (info.php, contact.php etc) when I make any changes.
What is a sensible back up to JQuery sliding divs for users without Javascript?
When I have understood you right, make a php-file with the static content. (The content on all sites) und include the content (info/contact) per include from another file depending on a GET Param like "page".
Hide the <div>s with jQuery so that users without JavaScript can still see all the <div>s in one long page. Users with JavaScript, on the other hand, can slide the <div>s as usual.
jQuery IS JavaScript - is cannot be a backup plan.
one does not simply use the terms JavaScript and jQuery interchangeably
jQuery is a JavaScript library. By disabling JavaScript, the jQuery scripts will not be able to hide the <div>s. The key is to keep it functional when JavaScript is not available. As long as you do not perform critical manipulation to the page that would render it non-functional without JavaScript, you can cater for those non-JavaScript users. In this case, putting the modification work over to jQuery (or JavaScript) is a way to preserve functionality.
At first add a class to_hide to all divs which should be hidden when javascript is activated.
The simplest way is to hide the divs like this on page load:
$(document).ready(function() {
$('.to_hide').hide();
});
Note that if you do this, the layout will blink when loaded (the full content will be shown at first and then the dynamic divs will be hidden).
To avoid blinking you can add css rule for to_hide class dynamically. Use the following function in the <head> to do that:
function dyn_css_rule(sSelector, sCssText) {
try {
var aSS = document.styleSheets;
var i;
for (i=aSS.length-1; i>=0; i--) {
var oCss = document.styleSheets[i];
var sMedia = (typeof(oCss.media) == "string")?
oCss.media:
oCss.media.mediaText;
if (!sMedia
|| sMedia.indexOf("screen") != -1
|| sMedia.indexOf("all") != -1
) {
break;
}
}
if (oCss.insertRule) {
oCss.insertRule(sSelector + " {" + sCssText + "}", oCss.cssRules.length);
} else if (oCss.addRule) {
oCss.addRule(sSelector, sCssText);
}
} catch(err) {
var tag = document.createElement('style');
tag.type = 'text/css';
try {
tag.innerHTML = sSelector + " {" + sCssText + "}";
} catch(err) {
tag.innerText = sSelector + " {" + sCssText + "}";
}
document.getElementsByTagName('head')[0].appendChild(tag);
}
return sSelector + "{" + sCssText + "}";
};
dyn_css_rule('.to_hide', 'display: none');
A Pure CSS Solution
This may or may not work depending on the situation, but you can actually mimic a drop-down menu's behavior with css selectors in IE8 and up. Here's an example. Click on the menu, and as long as you hover around the content the content will appear, no javascript required.
Functionality
By default, all the content is hidden. However, thanks to the :active pseudoclass, you can change the content to display when the parent is clicked. This is pretty inconvenient though - the user has to hold down the mouse to see anything. However, we can cheat a bit - by adding a :hover pseudoclass that displays the content, if the user clicks and hovers the content will stick around.
So far, we have this css:
.container.content {
display: none;
}
.container:active .content {
display: block;
}
.content:hover {
display: block;
}
This is a little flaky though - you have to move your mouse down over the content to have it persist, and will likely confuse. We can cheat a bit though by making the content larger than it appears. A simple way to do this would to be just to padding (that's what I've done in the example I added), but this can cause some odd reflow issues. A better way I think is to add deliberate spacing divs that add to the size of the content without changing the flow.
If we add this
<div style="position:absolute; top:-50px; height: 50px; width: 100%;"></div>
to the start of the content, there's an invisible div hovering over the menu, which will extend the area on which hover works. A similar thing can be done to the bottom, leaving us with a solution that has a larger hover area, and doesn't trigger reflows beyond the main content.
Remaining Problems
Anyway, this isn't perfect since it certainly isn't as flexible as javascript. There's no sliding, and you can't reliably make the content show up if the user mouses out.
As other people suggested, you can still improve this with javascript after the fact should the user have it enabled though - this can still work as a decent backup to noscript users.
I ended up using a solution that combines Antony's answer and this answer: https://stackoverflow.com/a/8928909/1342461
<html class="no-js">
<body>
<div id="foo"></div>
</body>
</html>
#foo
{
display: none;
}
html.no-js #foo
{
display: block;
}
$(document).ready(
function()
{
$('html').removeClass('no-js');
}
);
All the divs will be seen by people without javascript. Then, I can set my navigation links to a href="#info" for example, to get it to scroll down to the correct div for non-javascript users while doing "slide.down()" etc for javascript users.
Have your info.php main text in an include file. Lets say info.inc.php
When non-js user clicks the link, they go to info.php into which the include file is, well, included.
But when a js user clicks the link, you load the info.inc.php onto your div and only THEN show it with jquery.
Say
$('a.info').click(function(e){
e.preventDefault();
$('#infoDiv').load('info.inc.php')
.show();
return false;
});
When you need to update content, just update the include file.

Add an image to a javascript object

I am trying to create/invent a new javascript slider object which will work by displaying a base line image:
http://imgur.com/DuVkE.png
then I want to use these 'knobs' to layer on top depending on certain circumstances
http://imgur.com/GKkqx.png
These have already been 'cut up' and will be placed on one of the three black knobs. I have many different colors because I plan to run through them so that the color appears to transform from one, to the other.
So I need to be able to attach an image to the id I received from the user and then manipulate the image later.
My code:
<div id='option1'></div>
<script type="text/javascript">
var slide1 = new slider("option1");
My constructor will look something like this:
function slider(id) {
var obj = document.getElementById(id);
if (!obj) {
var state = -1;
return -1;
}
var state = 0; //blank state
//alert("in");
//alert(document.getElementById(id).className);
//this.addClass("hSliderBack"); INCORRECT SYNTAX!!!
$("#"+id).addClass("hSliderBack"); //this works
}
I fixed the problem with the addClass above, though a little ugly.
My CSS script:
.hSliderBack
{
background-image: url('/Switches/switchLine.png');
background-repeat: no-repeat;
padding-left: 2px; /* width of the image plus a little extra padding */
display: block; /* may not need this, but I've found I do */
}
This is how I can add a picture to my constructor. Still a lot of work to do, but at least it's a start. Any comments are still appreciated as I am very green!!
What you write here:
//obj.innerHTML = "<img src=' this doesn't seem right to me.
is in fact one perfectly reasonable and viable way. You enter into the DOM the <img> node referencing the image you want to display.
However, more common and perhaps more maintainable solution in many cases is to have a CSS style that references a background image, and you enter a <div> into the DOM using the style that causes your image to be displayed.
You should ask yourself, though, is it best to do this without any support from tools. Many of the most popular JavaScript libraries have tools like this built in, or at the very least, have methods that make building this type of code much, much easier.
Of course, if you are doing this to learn the basics of web development before using a framework so you understand what they are doing more thoroughly, more power to you :-)

Categories

Resources