Update CSS background-image Only When Source Image Changes Using Javascript - javascript

Is it possible to update the CSS image pointed by the passed-in url in "backgound-image: \"<some-url>\" in Javascript only when the source image is changed in the server. I.e. cache the image, and then retrieve the image from the server when it's needed to be reload and compare it with the cached image. Then, reload it when it's updated only. I know that in order to refresh an image in CSS through Javascript, the image must have a different string after the "?" in the image source, and one way to do so is by using the current time returned by Date.getTime method after "?". E.g. (I got this method from here https://stackoverflow.com/a/1077051/7000599):
$(".some-css-class").css("backgound-image", "url(" + "\"" + some_source + "?" + new Date().getTime() + "\"" + ")");
However, I would like to call the above logic when I need to update the background-image only when it detect the image has been changed. So is there a way to detect such changes?

Give each image a unique id and then poll the server every so often to see if there's a new id, and if so, change the underlying image.

You may add API to server side which returns some hash value for image content. So when you put the image for first time - add hash-value for specific image after '?'.
$(".come-css-class").css("backgound-image", "url(" + "\"" + some_source + "?" + imageHash + "\"" + ")");
Than from time to time call the hash API and replace background-image property. When image will be updated on server - it will return new hash-value - so css will reload the image.

To automatically update the image through JavaScript itself (without relying on any back-end script), you would need to poll the server periodically to check whether the modification date was greater than a cached copy of the image. You would then need to refresh the page if this was the case.
This would be done similar to the following:
var previous = null;
var current = null;
var image_url = 'IMAGE URL';
setInterval(function() {
var req = new XMLHttpRequest(); // Poll the server
req.open("HEAD", image_url, false);
req.send(null);
if (req.status == 200) {
current = req.getResponseHeader('Last-Modified'); // Get modification time
}
if (previous && current && previous !== current) { // If it's been modified
location.reload(); // Refresh
}
previous = current; // Store the 'new' image information
$(".come-css-class").css("background-image", "url(" + "\"" + image_url + "\""); // Update the image
}, 2000); // Repeat every two seconds
Also note that you called it backgound-image in your question. You need to ensure that you call it background-image in order to function correctly.
Hope this helps! :)

Similar to a reply comment to #Nosyara, I think the better approach to this problem I have found so far is by using the HTTP header ETag to detect whether the source image (or file as well) is changed from the last cached file. I think #ObsidianAge's answer would also work (i.e. using the Last-Modified header), but for some reason it doesn't show any changes when the source image (or file) is changed. I did a simple local Apache server and simple webpage with a Javascript onInterval event to console.log whether the file has changed or not, but it doesn't seem the case. So apparently ETag is a way to do it. Here's the Wikipedia page explaining it's purposes (it has other purposes too): https://en.wikipedia.org/wiki/HTTP_ETag.

Related

Is it possible to give html images more than one fallback url?

I found out that you can have a fallback URL for an <img> using onerror if the first loading fails.
But it is possible to provide a list of sources (urls) to try and keep trying until one of them loads?
I was messing around with this for about an hour and was working towards this solution with JavaScript. I think its very clunky and I wonder if there is more more pretty way of doing this.
My ugly approach
Each image has several data attributes such as fallback1, fallback2 with the alternatives sources to load from if loading fails. Every image also has this.onerror=null;this.src='error.jpg
So every image that fails to load will show the picture error.jpg instead.
So an image may look like this
<img src="https://i.imgur.com/iYNdJeW.jpg" onerror="this.onerror=null;this.src='error.jpg'" data-fallback1="https://i.imgur.com/iYNdJeW2.jpg" data-fallback2="https://i.imgur.com/iYNdJeW3.jpg" />
Call a script in window.onload that iterates over each image and check if the source ends with error.jpg.
If it does it takes the first fallback url in that image's data properties (fallback1) and changes the source to that. Then removes that data-property from the image (not sure if that is possible or meant to be done), because he has already been tried. Then the script recursively runs again.
The recursion stops if if no images' source ends in error.jpg OR If all images whose source ends with error.jpg do not have any data attributes (no more alternative sources to try).
I think it would work, but it seem very very hackish.
I found out about <picture> and <source> and was very optimistic, but <picture> just accepts a 404 if the image does not load.
Has anyone come up with a better approach for doing this? Ideally I wish you could give img a list of urls and it would just keep trying until it got a non-error.
You're setting onerror to null when your first error is fired. Onerror will automatically fire if you change the source and it fails to load again. You could just store the list of fallbacks and increment an index each time onerror is fired.
Here's an example, and you could easily convert this to store all of the different variables directly on the element. I've provided 4 fake URLs and a final placeholder image as a real URL. You'll see it does load the placeholder image.
var fallbacks = ["https://example.com/badurl1.jpg", "https://example.com/badurl2.jpg", "https://example.com/badurl3.jpg", "https://via.placeholder.com/350x150"];
var index = 0;
document.querySelector("img").onerror = function(){
if(index >= fallbacks.length) return;
let next = fallbacks[index];
this.src = next;
index++;
};
<img src="https://example.com/badurl.jpg"/>
This will cycle through a list until your image stops getting an error
to edit it, change the 'fallbacksrc' array
HTML
<img src="*" id="img" onerror="ImgOnError()"></img>
JS
let fallbacksrc=[]; //All All Fallbacks Here, when error will start at the second one because 1st one is already tried
let fallbackcount = 1;
let Img = document.getElementById('img')
function ImgOnError(){
if(fallbackcount >= fallbacksrc.length){
fallbackcount = 0
Img.src=fallbacksrc[fallbackcount]
}else{
Img.src=fallbacksrc[fallbackcount]
}
fallbackcount++;
}
I've come up with a vanilla-JavaScript way where you don't need to specify the image in javascript.
Define this function:
function incrementFallbackSrc(img, srcs) {
if (typeof img.fallbackSrcIndex === 'undefined') img.fallbackSrcIndex = 0;
img.src = srcs[img.fallbackSrcIndex++];
}
and for each image, pass all the fallback URLs in the function call.
<img
src="https://i.imgur.com/iYNdJeW.jpg"
onerror="javascript: incrementFallbackSrc(this, ['https://i.imgur.com/iYNdJeW2.jpg',
'https://i.imgur.com/iYNdJeW3.jpg',
'error.jpg'])">
>
to set it programmatically, you may have to put the function inside the definitiaion like so:
img.setAttribute('onerror', "function incrementFallbackSrc(img, srcs) {if (typeof img.fallbackSrcIndex === 'undefined') img.fallbackSrcIndex = 0;img.src = srcs[img.fallbackSrcIndex++];}; incrementFallbackSrc(this, ['" + fallbackUrl1 + "," + fallbackUrl2 + "'])");

Changing images src by javascript - cache issue

I am building a web site, using jQuery.
Two major things:
I want to change picture (image src) manually, but sometimes the picture isn't changed (I am using right now chrome, but I want a general solution).
Also, I want to get the image width + height, after I load the picture.
(The picture is a file from the server - I just use different file name).
When changing the image by JavaScript/jQuery - I realized that when I change the image once, it is kept in memory, so I run into some problems.
I have found that the image is not uploaded the second time due to cache problem, so as I did some workaround, I realized that I need to do JavaScript command such as:
$("#id_image").attr("src", "pictures\\mypicture.jpg" + "?" + Math.random());
That's how I am changing manually the image.
By using the Math.random() I got a second problem:
If I wrote before Math.random() the line:
$("#id_image").width("auto");
$("#id_image").height("auto");
I don't get the height + width after using the Math.random(), so I put another line, and finally my code is:
$("#id_image").width("auto");
$("#id_image").height("auto");
$("#id_image").attr("src", "pictures\\mypicture.jpg");
$("#id_image").attr("src", "pictures\\mypicture.jpg" + "?" + Math.random());
alert("#id_image").width()); // **** returns 0 sometimes due cache
alert("#id_image").height()); // **** returns 0 sometimes due cache
Still, I have some problem (see remarks on asterisk), and I don't know how to always get the width + height of loaded image.
You could try setting onload handler before setting image src, this way, even your image is cached, this should give you correct image size:
$("#id_image").on('load',function(){
alert($(this).width());
alert($(this).height());
});
$("#id_image").attr("src", "pictures\mypicture.jpg");
VersionNumber = System.Configuration.ConfigurationManager.AppSettings["Version"];
//Caching Issues For JS
public static string JSVersionUpdate(string JsFile)
{
StringBuilder str = new StringBuilder();
str.Append(GetQualifiedPath(JsFile));
str.Append("?Version=");
str.Append(VersionNumber);
return str.ToString();
}
public static void DiscardVersion()
{
VersionNumber = null;
}
//get Full Path for JS File
private static string GetQualifiedPath(string path)
{
var httpRequestBase = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current);
path = path.Replace("~", string.Empty);
string appPath = string.Empty;
if (httpRequestBase != null)
{
//Formatting the fully qualified website url/name
appPath = string.Format("{0}://{1}{2}{3}{4}",
httpRequestBase.Request.Url.Scheme,
httpRequestBase.Request.Url.Host,
httpRequestBase.Request.Url.Port == 80 ? string.Empty : ":" + httpRequestBase.Request.Url.Port,
httpRequestBase.Request.ApplicationPath,
path);
}
appPath = appPath.TrimEnd('/');
return appPath;
}
}
In UI Page : script src="JSVersionUpdate("~/Scripts/Application/Abc.js")"
O/p : ~/Scripts/Application/Abc.js/version=1.0
Browser request for JS Files from server only if Version is Different else it will cached out.Now i don't need to tell someone to clear the cache again and again after deployment.Only thing i do is i change Version Number in Web COnfig.
Same can be applied to Image As well !! Hope it Helps

exchange variables between html files in javascript

I am trying to share variables between two html pages. I am only using javascript and HTML5 to develop a windows 8 app. Based on an image which a user clicks on one page, I want a div on a second page to be populated with that image. Any ideas?
When I click on the image, I am currently calling the following function:
function imageClick(url) {
//var id = parsed.ClientID;
//window.location= url + "?" + id
window.location = url;
}
Then in my html file, I have this line:
<img onclick="imageClick('pages/page2/page2.html')"
data-win-bind="src:image" style="width: 133px; height: 125.5px;">
I was thinking of getting that id in the next page's url (if I were to uncomment the commented lines above) but it's a bit hackky and I don't actually know how to go about executing the retrieval of that on the next page..
Is there a more efficient and easy way of doing this in javascript? Like an equivalent of sessions in php or something?
Javascript does not have session variables because it runs on the client side. You can use URL parameters and cookies in order to achieve the same results.
You can get the URL parameter by using this function:
http://ziemecki.net/content/javascript-parsing-url-parameters
Add the link to the image to the query part of the url when they click. Something like you had in the comment, assuming you don't have a query part already:
function imageClick(url) {
//var id = parsed.ClientID;
window.location= url + "?src=" + url.src;
//window.location = url;
}
The other page can use window.location.search to extract it, strip off the src=. The code would look something like this:
var src = window.location.search;
if (src.indexOf("src=") == 0) {
src = src.substring(4);
}
newPageImageElement.src = src;
Where newPageImageElement is the <img> where you want to display the picture on the second page.

How should I create relative paths in javascript using MVC3?

I am having some difficulty aligning my paths without a hardcode in javascript. I am running an asp.net MVC3 web application.
If my path is of the form
var url = 'http://serverNameHardcode/websiteNameHardcode/service/service?param1=' + param;
Then things work fine when I do
$.get(url,
{},
function (data) {alert('callback success');},'json');
I would like to create a relative path. I tried
var url = 'service/service?param1=' + param;
And this works when I run locally and also in Firefox, but not in IE7. When I publish to the server without the hardcode the callback never fires. I know MVC-3 adds some complexity to routing, but I do not know if it applies to this situation; so, I marked this question as such.
How should I setup my path so I don't need hardcodes?
Just write out the app path as a global js variable from your master view, then compose links as
APPPATH + "path/whatever"
Just had to solve this for one of my jQuery plugins, where it is preferable not to modify anything global (i.e. outside the scope of the plugin use) so I had to disregard the marked answer.
I also found that because I host DEV locally in IIS I could not use a root-relative path (as localhost is not the root).
The solution I came up with extended what I had already started with: a data-controller attribute specifying which controller to use in the element I am applying my plugin to. I find it preferable to data-drive the controller names so the components can be more easily reused.
Previous:
<div data-controller="Section">
Solution:
<div data-controller="#Url.Content("~/Section")">
This injects the server root (e.g. /Test.WindowsAzure.Apr2014/ before the controller name so I wind up with /Test.WindowsAzure.Apr2014/Section which is perfect for then appending actions and other parameters as you have. It also avoids having an absolute path in the output (which takes up extra bytes for no good reason).
In your case use something like:
// Assuming $element points to the element your plugin/code is attached to...
var baseUrl = $element.data('controller');
var url = baseUrl + '/service?param1=' + param;
Update:
Another approach we now use, when we do not mind injecting a global value, is Razor-inject a single global JavaScript variable onto window in the layout file with:
<script>
window.SiteRoot = "#Url.Content("~/")";
</script>
and use it with
var url = window.SiteRoot + '/service?param1=' + param;
One option:
var editLink = '#Url.Action("_EditActivity", "Home")';
$('#activities').load(editLink + "?activityID=" + id);
another example:
var actionURL = '#Url.Action("_DeleteActivity", "Home")';
$('#activities').load(actionURL + "?goalID=" + gID + "&activityID=" + aID);
If you don't need to add to the string:
$('#activities').load('#Url.Action("_Activities", "Home", new { goalID = Model.goalID},null)');
I really need the path to get this to work, maybe its IE7. Who knows. But this worked for me.
Grab the URL and store it somewhere. I chose to implement the data attribute from HTML5.
<div id="websitePath" data-websitePath='#Request.Url.GetLeftPart(System.UriPartial.Authority)#Request.ApplicationPath'></div>
Then when you need to perform some AJAX or otherwise use a URL in javascript you simply refer to the stored value. Also, there are differences in the versions of IIS (not cool if your devbox is IIS5 and your server is IIS7). #Request.ApplicationPath may or may not come back with a '/' appended to the end. So, as a workaround I also trim the last character if it is /. Then include / as part of the url.
var urlprefix = $('#websitePath').data('websitepath');
urlprefix = urlprefix.replace(/\/$/, "");
var url = urlprefix + '/service/service?param1=' + param;
While the accepted answer is correct I would like to add a suggestion (i.e. how I do it).
I am using MVC, and any ajax request goes to a controller. My controllers have services so if a service call is required the controller will take of that.
So what's my point? So if ajax always communicates with a controller, then i would like to let the MVC routing resolve the path for me. So what I write in Javascript for url is something like this:
url: 'controller/action'
This way there is no need for the root path etc...
Also, you can put this in a separate Javascript file and it will also work whereas #Url.Content will need to be called on the view.

How can you read a file line by line in JavaScript?

I'm writing a web-app for the iPad that will be loading data from a text file. (A sample data set is around ~400 kb). I have everything set up except the file reading. The way I have set up my code, you pass an object which reads a file line by line.
How can I read a file line by line?
If there is no direct way to read a file line by line, can someone please show me an example of how to read a file into a string object? (so that I can use the split method :P)
This could work, if I understood what you want to do:
var txtFile = new XMLHttpRequest();
txtFile.open("GET", "http://website.com/file.txt", true);
txtFile.onreadystatechange = function()
{
if (txtFile.readyState === 4) { // document is ready to parse.
if (txtFile.status === 200) { // file is found
allText = txtFile.responseText;
lines = txtFile.responseText.split("\n");
}
}
}
txtFile.send(null);
Mobile Safari doesn't have the File API, so I assume you're talking about reading from a web resource. You can't do that. When you read a resource via ajax, the browser will first read it fully into memory and then pass the entire string to your ajax callback as a string.
In your callback, you can take the string and break it into lines, and wrap that up in an object that has the API that your code wants, but you're still going to have the string in memory all at once..
With jQuery:
myObject = {}; //myObject[numberline] = "textEachLine";
$.get('path/myFile.txt', function(myContentFile) {
var lines = myContentFile.split("\r\n");
for(var i in lines){
//here your code
//each line is "lines[i]"
//save in object "myObject":
myObject[i] = lines[i]
//print in console
console.log("line " + i + " :" + lines[i]);
}
}, 'text');
i dont think thats possible until you use ajax to hit some server side code.

Categories

Resources