Chrome doesn't cache images inside SVG - javascript

I just discovered that Chrome doesn't cache images which are placed inside SVGs if their cache-control header is set to no-cache. Firefox & IE10 seem to ignore this setting.
I've created a little test page with a static SVG:
HTML:
<div style="width: 500px; text-align: center;">
<input id="move-left-btn" type="button" value="<<">
<input id="move-right-btn" type="button" value=">>">
</div>
<div class="svgwrapper" style="width: 500px; height: 250px; background-color: lightgrey;">
<svg id="svg" version="1.1" xmlns="http://www.w3.org/2000/svg" width="500" height="250">
<g id="svggroup" class="transition-on" transform="matrix(0.2,0,0,0.2,80,35)">
<image width="1672" height="887" opacity="1" xlink:href="https://dl.dropboxusercontent.com/sh/q7htlj5h8qqfhjf/SVDuynM7R3/car.png"></image>
</g>
</svg>
</div>
Javascript:
$(document).ready(function() {
var curXPos = 80;
// Local test function which represent some server calls in my "real life" scenario
// Just updates the x-position in the transform matrix in this test case
function updateSvgText(svgText, posXDelta) {
curXPos += posXDelta;
if (curXPos < 0) {
curXPos = 160;
} else if (curXPos > 160) {
curXPos = 0;
}
return svgText.replace(/matrix\(.*\)/, 'matrix(0.2,0,0,0.2,' + curXPos + ',35)');
}
// Fetch the new SVG (in real life from server) and rerender it
function moveSvg(posXDelta) {
var svg = $('#svg'),
svgText = updateSvgText($('.svgwrapper').html(), posXDelta);
svg.empty();
svg.append($(svgText).children());
}
$('#move-left-btn').click($.proxy(moveSvg, this, -20));
$('#move-right-btn').click($.proxy(moveSvg, this, 20));
});
Working example with cache-control header of source image set to no-cache (flickers in chrome after every press on the "move" buttons):
http://jsfiddle.net/zF6NF/4/
Same example with different source image with cache-control header set to max-age=315360000,public (no flickering):
http://jsfiddle.net/zF6NF/5/
In Chrome you can see the reloading of the images on each button click in the first example ("flickering" of the image & visible in the network tab of the dev tools) whereas Firefox rerenders the SVG in both examples smoothly without any reloading.
Some additional information:
This is just an example. In my "real-life-scenario" I receive a new SVG from the server (instead of the updateSvgText method call) which means that I can't just perform partial updates of the SVG by changing the value of the transform matrix attribute but have to rerender the whole SVG every time (at least by now...).
I can't control where the images come from which means 2 things:
I can't change the cache-control header
I can't create Base64 encoded data-uris, save them locally and just replace the images inside the SVG with those data-uris before rendering (can't create Base64 encoded data-uri because of "Same resource origin" policies...)
Is there any way to either...
Overwrite/overrule the cache-control header locally even if the image is from an uncontrolled remote location?
Create the Base64 encoded data-uri from an Image that is loaded from a different domain I don't have any control over client sided?
Somehow tell Chrome to always cache images inside my SVGs?
Needless to say that other solutions are also very welcome!
Thanks

Unfortunately, when it comes to caching, it's 99% the server's job.
In-dept guide : here
Browsers will always look for more recent versions of the file based on certain conditions:
The cached entry has no expiration date and the content is being accessed for the first time in a browser session
The cached entry has an expiration date but it has expired
Cache-Control/Pragma tells the browser not to cache
Etag in header is a pain.
In terms of solutions you have:
Be very insistent to your server guys that you need caching
(remove etag, Cache-Control: public,max-age=31536000, Pragma: public)
Create a proxy on your domain that requires the image from the site, (optionally convert to base64) then send to your client (with the proper headers). Here's an example for PHP : here

Related

How do I get screen width (or windows width) to PHP ASAP?

First of all, if there's a better way, I'm open to suggestions.
I am allowed to change frontend, but not backend (CMS code). Every subpage of the website has an image that is loaded at the top of the page, just bellow navigation bar (after header.php). I can choose the image in CMS and it's path is stored in database.
So let's say an image (1920x1080) is named "image.jpg". I need to load a resized image (640x360), which I generically named "image.mob.jpg" (and stored in the same place on server as original "image.jpg") when image is loaded on a mobile device (really when screen width is less or equal 640px - I chose the number, perhaps it would be better to use 480px or 360px).
What I currently do
HTML
<body>
...
<?php
...
while ($row = mysqli_fetch_array($result)) {
if (isset($_SESSION['screen_width']) && ($_SESSION['screen_width'] <= 640)) {
// change "path/to/image.jpg" to "path/to/image.mob.jpg"
$row['image'] = substr($row['image'], 0, -4).".mob".substr($row['image'], -4);
} ?>
<div class="image" style="background-image: url('<?=$row['image']?>)"> ... </div>
...
<?php
if (isset($_SESSION['screen_width']) AND isset($_SESSION['screen_height'])) {
// echo 'Screen width: ' . $_SESSION['screen_width'];
} else if (isset($_REQUEST['width'])) {
$_SESSION['screen_width'] = $_REQUEST['width'];
header('Location: ' . $_SERVER['PHP_SELF']);
} else {
echo '<script type="text/javascript">window.location.href = window.location.href+"?width="+screen.width;</script>';
}
// echo $_SESSION['screen_width'] = "<script>document.write(screen.width)</script>"; ?>
</body>
This is somewhat simplified but basically what happens is this. On first visit, there is no width stored in $_SESSION so it loads the big "image.jpg". But before it actually loads it, it already executes javascript and reloads the page with $_GET variable width in the URL. All visits but the first already have width stored in session, so the URL is always SEO friendly again. Also image path is renamed and the site loads "image.mob.jpg" if screen width is 640px at most.
This works perfectly.
The problem is I need to have all site URLs SEO friendly at all times, so it's not allowed to have it "https://seo-friendly.url?=width={screen.width}", even if only just once.
As you can see in the last commented line I also tried with echo $_SESSION['screen_width'] = "<script>document.write(screen.width)</script>";, but obviously it doesn't work on first page load since all PHP is executed before any JavaScript.
How do I solve this with AJAX (I need the whole code, cause I'm to dumb to understand other's internet answers sigh) or if there is any better alternative.
Thank you in advance.
As #Paflow pointed out, to do this in PHP is an unnecessary hassle and not the recommended or suited way of doing it.
It is close to virtually impossible to get the viewport on an HTTP GET request. There are certain hacky ways of achieving this, but they need some sort of extra requests via JS.
Your best option is to use HTML.
srcset will have you covered!
I would suggest something like:
<img srcset="/image.jpg 640w" sizes="100vw" src="/image.mob.jpg" alt="Alt attribute. Fill me!">
This will, by default load you image.mob.jpg image, which will be served then as a performant base image, and if your browser supports srcset and the viewport is bigger than 640px, you will get the /image.mob.jpg image.
Say you want more sizes, then all you have is to add them to srcset attribute and specify different sizes:
<img srcset="/image-huge.jpg 1990w, /image-big.jpg 1440w, /image.jpg 640w" sizes="100vw" src="/image.mob.jpg" alt="Alt attribute. Fill me!">
MDN responsive images
While the answer of #avcajaraville is very useful in general, it is the comment of #Paflow that helped me find the solution. No JavaScript involved.
HTML
<?php
...
while ($row = mysqli_fetch_array($result)) {
$mobile_image_path = substr($row['image'], 0, -4).".mob".substr($row['image'], -4);
$image_id = "banner-image-" . $row['id_banner']; ?>
<style>
#<?=$image_id?> {
background-image: url('<?=$row['image']?>');
}
#media (max-width: 640px) {
#<?=$image_id?> {
background-image: url('<?=$mobile_image_path?>');
}
}
</style>
<div class="image" id="<?=$image_id?>"> ... </div>
I first used <style type="text/css" scoped>, but then I read scoped is deprecated and using just <style> inside of <body> is allowed.
Not the fanciest way, but it works!

GAE + Blobstore: Serve different image size depending on viewport width

As we know, we can serve images from the blobstore using its serving url, and append =sXX to indicate the image's width:
<!-- serving an image of 600px width -->
<img src="{{serving_url}}=s600" alt=""/>
This works well, except we want to serve a smaller image for smaller screens for obvious webperf reasons. How can we serve a different image width depending on screen size?
Thanks for any tips.
You can pull this off easily using javascript, the basic technique is to change the src attribute after evaluating the media queries you're interested in.
For starters, I would avoid filling the src of the image until evaluation, so the full image doesn't load by default; something like this:
<img data-src="{{serving_url}}" height="240" width="320">
Here we rendered the url in a data attribute so we have access to it later in the script. I'm also adding a default size so the space is reserved, but that's optional of course.
Now, we can use Window.matchMedia() to evaluate our query strings:
var mql = window.matchMedia('(max-device-width: 667px)')
if (mql.matches) {
// we're probably running on an iPhone6!
img.src = img.dataset.src + '=s667'
}
The image is fetched using the perfect size!
I'm including a working snippet, it can be easily extended for many query tests for you to try.
var img = document.querySelector('img')
, mql = window.matchMedia('(max-device-width: 667px)')
, size = 1200
if (mql.matches) {
size = 667
}
img.src = img.dataset.src + '=s' + size
img {
object-fit: contain;
}
<img
data-src="http://lh4.ggpht.com/TgjDT-cPRr6bjrpSVQeILk93o4Ouzjo1ygMB6KpmnCHhyH5vJKKRrqxCD2bC3T09CRIP6h5QFsV_l0hnhio5bN7z"
height="240"
width="320"
>
The test image was kindly "provided" by this question. I'm also using object-fit because it makes sense (to me).

css - change an image frequently with less server requests

I have a in html. Its content is described in css.
With different functions the classname of the div is changed to imageholder1,2 etc.
.imageholder1 { content: url('image1.png')}
.imageholder2 { content: url('image2.png')}
But I think this causes multiple server requests , each time the classname is changed.
Is there a way to reduce the server requests to the image file.
I would like a way to store the image into a variable and then point it from css code. Is it possible?
I also tried to use a css image sprite but I need to need a more flexible way.
If you put all your images in a hidden (display none) div, they'll load all at once. The browser should cache them, so then each time it changes, it pulls from the client system.
<div style="display: none;">
<img src="image1.jpg">
<img src="image2.jpg">
<img src="image3.jpg">
</div>
Preload the images and it will cache the images and only request each image once (it will also allow for smoother transitions between the images because subsequent images will already be downloaded). This will limit your http requests to one request per image (the minimum you can achieve without sprite sheets).
Here is a really nice jQuery solution (that doesn't add hidden elements to the DOM) but really any preloading scheme should work:
$.preloadImages = function() {
for (var i = 0; i < arguments.length; i++) {
$("<img />").attr("src", arguments[i]);
}
}
$.preloadImages("hoverimage1.jpg","hoverimage2.jpg");
Reference
According to me, if we load multiple images on a page it is going to have multiple requests.
Instead of this simply use a sprite combining all the images.
This will cause to fetch a single image from server.
Then define css properties for background for classes such that they will have different position values of the sprite used causing to show required image.
.imageholder1 {
background: url(/path/to/sprite.png) -60px -120px no-repeat;
}
.imageholder2 {
background: url(/path/to/sprite.png) -20px -120px no-repeat;
}

Unable to scale pdfobject element down to fit in a given frame

I have some problems with including an PDF on an JSF site. I'm using pdfobject (http://pdfobject.com/) but it does not work out the way I want. I can't adjust my settings for my desired outcome, where there is a unscrollable, scaled document (so that it fits the frame) shown in a frame with width = 595px & height = 842px. I tried until now:
<script type="text/javascript" src="pdfobject.js"></script>
<script type="text/javascript">
window.onload = function() {
var myPDF = new PDFObject({
url : "ls_v7.pdf",
width: "595px",
height: "842px",
pdfOpenParams : {
page: 1,
view : 'FitB'
}
}).embed("pdf");
};
</script>
[...]
<div id="pdf">
It appears you don't have Adobe Reader or PDF support in this web
browser. Click here to download the PDF
</div>
or also
<object data="ls_v7.pdf#page=1&view=fitH"
type="application/pdf" width="595" height="842"> </object>
I think I tried most of the cases mentioned in this document, but where not able to come up with the right solution. (The oucome is always a frame with scroll-bars & the PDF gets always shown in full size)
Some background information:
I first included an PDF with iFrame on a JSF page, what went very well. But the Problem is, that on IE8 (some of the client machines) does not show them. So I went to this approach, if you would suggest to try another framework/etc. I would be also very greatful.
it's best to use CSS to specify exact size of the wrapper element.
PDFObject expands the PDF to 100% width/height of the parent container, so wrap your PDF in a div then size the div using CSS.
See this example: http://pdfobject.com/examples/simplest-styled.html

Preload Images faster

I'm building a website that contains divs with background images. I'm very new to JavaScript. I want to preload the images so when you go to the site you don't have to wait and view a blank box when the image is loading. I'm using this preload code, but when I go to the site the images are still loading slowly. Is there a way to make this faster?
<script>
$(document).ready( function() {
var c = new Image();
c.onload = function(){
$("#contenthome").css("background-image", "url(../Images/Homepage.png)");
}
c.src = "url(../Images/Homepage.png)";
});
</script>
This is not directly approachable but has two solutions.
This question's solution needs some understanding about server side request headers specifying caching information. I am used to appengine so I set expiry to about a month or so. Caching is all about suggesting a browser to "keep the following images with you for a while, don't take from me each time you need them". according to your server side language, it is worth spending so that you will master this important thing in browser environment.
And, you must play some trick if your user should not see a set of blank boxes while the images load. in fact, you style them invisible, and once loaded, you make them visible. since you are using jquery, you can use something like
$(window).load(
function(){ $('#showAfterLoadingComplete').show("slow").fadeIn(); }
A page of example is here
Try this:
<script>
$(function() {
var c = new Image();
$(c).load(function(){
$("#contenthome").css("background-image", "url(../Images/Homepage.png)");
$(this).attr('src','../Images/Homepage.png').show();
}).hide();
});
</script>
I would suggest employing a different method. Javascript has to wait for the page to load in order to work right, which inherently will introduce a delay.
Why not base 64 encode all your images into css classes in a dedicated css document.
You can use a site like this to convert your images:
http://webcodertools.com/imagetobase64converter
Then paste the Data URI into the background-image property in your css class.
Your eventual markup could look something like this:
<div id="contenthome">[some content]</div>
And your CSS would look something like this:
#contenthome {
background: [DATA URI] /*I didn't paste the actual URI here, as it would be quite long and unruly */
no-repeat
50% 50%;
background-size: contain;
display: inline-block;
height: auto;
width: auto;
}
You may have to play around with the height, width, background-size, and display properties to get it to show just right.
You could even take a further step and use a cache manifest to explicitly cache your css files so the load times are even faster.

Categories