Is it possible to reload an image with an identical file name from a server using jQuery?
For example, I have an image on a page, however, the physical image can change based on user actions. Note, this does not mean the file name changes, but the actual file itself.
ie:
User views image on default page
User uploads new image
Default image on page does not change(I assume this is due to the file name being identical, the browser uses the cached version)
Regardless of how often the code below is called, the same issue persists.
$("#myimg").attr("src", "/myimg.jpg");
In the jQuery documentation, the "load" function would be perfect if it had a default method of firing the event as opposed to binding a callback function to a successful/complete load of an element.
Any assistance is greatly appreciated.
It sounds like it's your browser caching the image (which I now notice you wrote in your question). You can force the browser to reload the image by passing an extra variable like so:
d = new Date();
$("#myimg").attr("src", "/myimg.jpg?"+d.getTime());
It's probably not the best way, but I've solved this problem in the past by simply appending a timestamp to the image URL using JavaScript:
$("#myimg").attr("src", "/myimg.jpg?timestamp=" + new Date().getTime());
Next time it loads, the timestamp is set to the current time and the URL is different, so the browser does a GET for the image instead of using the cached version.
This could be one of the two problems you mention yourself.
The server is caching the image
The jQuery does not fire or at least doesn't update the attribute
To be honest, I think it's number two. Would be a lot easier if we could see some more jQuery. But for a start, try remove the attribute first, and then set it again. Just to see if that helps:
$("#myimg").removeAttr("src").attr("src", "/myimg.jpg");
Even if this works, post some code since this is not optimal, imo :-)
with one line with no worries about hardcoding the image src into the javascript (thanks to jeerose for the ideas:
$("#myimg").attr("src", $("#myimg").attr("src")+"?timestamp=" + new Date().getTime());
To bypass caching and avoid adding infinite timestamps to the image url, strip the previous timestamp before adding a new one, this is how I've done it.
//refresh the image every 60seconds
var xyro_refresh_timer = setInterval(xyro_refresh_function, 60000);
function xyro_refresh_function(){
//refreshes an image with a .xyro_refresh class regardless of caching
//get the src attribute
source = jQuery(".xyro_refresh").attr("src");
//remove previously added timestamps
source = source.split("?", 1);//turns "image.jpg?timestamp=1234" into "image.jpg" avoiding infinitely adding new timestamps
//prep new src attribute by adding a timestamp
new_source = source + "?timestamp=" + new Date().getTime();
//alert(new_source); //you may want to alert that during developement to see if you're getting what you wanted
//set the new src attribute
jQuery(".xyro_refresh").attr("src", new_source);
}
This works great! however if you reload the src multiple times, the timestamp gets concatenated to the url too. I've modified the accepted answer to deal with that.
$('#image_reload_button').on('click', function () {
var img = $('#your_image_selector');
var src = img.attr('src');
var i = src.indexOf('?dummy=');
src = i != -1 ? src.substring(0, i) : src;
var d = new Date();
img.attr('src', src + '?dummy=' + d.getTime());
});
Have you tried resetting the image containers html. Of course if it's the browser that is caching then this wouldn't help.
function imageUploadComplete () {
$("#image_container").html("<img src='" + newImageUrl + "'>");
}
Some times actually solution like -
$("#Image").attr("src", $('#srcVal').val()+"&"+Math.floor(Math.random()*1000));
also not refresh src properly, try out this, it worked for me ->
$("#Image").attr("src", "dummy.jpg");
$("#Image").attr("src", $('#srcVal').val()+"&"+Math.floor(Math.random()*1000));
Using "#" as a delimiter might be useful
My images are kept in a "hidden" folder above "www" so that only logged users are allowed access to them. For this reason I cannot use the ordinary <img src=/somefolder/1023.jpg> but I send requests to the server like <img src=?1023> and it responds by sending back the image kept under name '1023'.
The application is used for image cropping, so after an ajax request to crop the image, it is changed as content on the server but keeps its original name. In order to see the result of the cropping, after the ajax request has been completed, the first image is removed from the DOM and a new image is inserted with the same name <img src=?1023>.
To avoid cashing I add to the request the "time" tag prepended with "#" so it becomes like <img src=?1023#1467294764124>. The server automatically filters out the hash part of the request and responds correctly by sending back my image kept as '1023'. Thus I always get the last version of the image without much server-side decoding.
Based on #kasper Taeymans' answer.
If u simply need reload image (not replace it's src with smth new), try:
$(function() {
var img = $('#img');
var refreshImg = function(img) {
// the core of answer is 2 lines below
var dummy = '?dummy=';
img.attr('src', img.attr('src').split(dummy)[0] + dummy + (new Date()).getTime());
// remove call on production
updateImgVisualizer();
};
// for display current img url in input
// for sandbox only!
var updateImgVisualizer = function() {
$('#img-url').val(img.attr('src'));
};
// bind img reload on btn click
$('.img-reloader').click(function() {
refreshImg(img);
});
// remove call on production
updateImgVisualizer();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<img id="img" src="http://dummyimage.com/628x150/">
<p>
<label>
Current url of img:
<input id="img-url" type="text" readonly style="width:500px">
</label>
</p>
<p>
<button class="img-reloader">Refresh</button>
</p>
I may have to reload the image source several times. I found a solution with Lodash that works well for me:
$("#myimg").attr('src', _.split($("#myimg").attr('src'), '?', 1)[0] + '?t=' + _.now());
An existing timestamp will be truncated and replaced with a new one.
If you need a refresh of the exact URL and your browser has the image cached, you can use AJAX and a request header to force your browser to download a new copy (even if it isn't stale yet). Here's how you'd do that:
var img = $("#myimg");
var url = img.attr("src");
$.ajax({
url: url,
headers: { "Cache-Control": "no-cache" }
}).done(function(){
// Refresh is complete, assign the image again
img.attr("src", url);
});
Nothing else worked for me because while appending a token to the query string would download the new image, it didn't invalidate the image in the cache at the old URL so future requests would continue to show the old image. The old URL is the only one sent to the browser, and the server was directing the client to cache the image for longer than it should.
If this still doesn't refresh the image for you, see if this answer helps. For more information, here is documentation on the Cache-Control request header.
In the html:
foreach (var item in images) {
<Img src="#Url.Content(item.ImageUrl+"?"+DateTime.Now)" >
}
I simply do this in html:
<script>
$(document).load(function () {
d = new Date();
$('#<%= imgpreview.ClientID %>').attr('src','');
});
</script>
And reload the image in code behind like this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
image.Src = "/image.jpg"; //url caming from database
}
}
Related
My problem is that the backend server (written in grails) is automatically converting my request URL to be a different URL. Specifically, it is changing it from /UXChallengeAwards/processSelectedNotifications to /UXChallengeAwards/index.
--
In a template gsp file, I have defined a button that makes a jQuery ajax call when clicked on:
<button class="blue-link"
onclick="jQuery.ajax({type:'POST',
data:jQuery(this).parents('.multiSelectForm').serialize(),
url: '/ici/UXChallengeAwards/processSelectedNotifications/${challenge.id}',
success:function(data,textStatus){},
error:function(xhr,textStatus,errorThrown){}
})" >
The method UXChallengeAwardsController.processSelectedNotifications exists. It performs some work and then redirects to another action in the controller. In fact, this used to work. But somehow in the process of adding a second button I made a change which seems to have broken things.
When the button is now clicked, the request URL gets switched to /ici/UXChallengeAwards/index and a 404 is returned because index does not exist as an action in this controller.
I've googled, and the most common answer for when this happens is that a controller must return some results for the view. But I've seen plenty of examples of redirects in controllers, and I do not see what I am doing wrong. (I did try variants of rendering results, but with no success.)
Here is what my controller action looks like:
def processSelectedNotifications = {
def challenge
def checkboxes = params.list('selectCheckbox');
for (checkbox in checkboxes) {
// the checkbox contains the id of a ChallangeAward that should be published
ChallengeAwards challengeAwards = ChallengeAwards.get(checkbox.toInteger())
if (challengeAwards) {
// grab a challenge for use in the redirect, they are all the same
challenge=challengeAwards.challenge
publish(challengeAwards)
}
}
if (challenge) {
redirect action: 'challengeAwardsRemote', id: challenge.id
return
}
// render a failure message if we got here
render messageNS(code:"UX.ChallengeAwards.Publish.failure")
}
I would really appreciate any insights into what might be wrong, or how to go about tackling this issue. I've checked my UrlMappings, and this is the rule that should handle this controller/method request:
"/$controller/$action?/$id?"{ constraints {} }
Thank you very much!
I'm going to go ahead and answer my own question, in case it is helpful for other newbies.
It turns out that I was not getting an automatic redirect. Rather, I had an error in the button setup code, so that grails was using its default link behavior. (Which is to go to the controller that matches the view, and if no action is specified, use the index method.)
The code above was originally created using a remoteSubmit tag, but I found that the generated code did not support handling multiple forms on a single page very well. So, I copied that generated code and then tweaked it to handle the multiple forms. However, I wanted the styling to match up with what was already in place on the page, so I switched it to be a button. That's when things went awry.
Eventually, I ended up specifying an onClick function for the button, and then writing the ajax submit code in javascript. Which turned out to be much simpler.
Here is what the button specification ended up looking like:
<button type="submit" id="notifications" class="blue-link" >
<i class="fa fa-envelope-o"></i>
<g:messageNS
code="UX.DiscussionBoard.ChallengeAward.Button.notify" />
</button>
And the associated JavaScript:
jQuery(document).ready(function() {
var clkBtn = "";
jQuery('button[type="submit"]').click(function(evt) {
clkBtn = evt.target.id;
});
jQuery('.multiSelectForm').submit(function() {
var url = '/ici/UXChallengeAwards/processSelectedNotifications';
if (clkBtn == 'deletes') {
url ='/ici/UXChallengeAwards/processSelectedDeletes';
}
var errorTarget = jQuery(this).parents().find('.recipientMessage').val();
var requestData = jQuery(this).parents('.multiSelectForm').serialize();
var options = {
data : requestData,
type : 'POST',
url : url,
target : '#awardsTab',
error : function(data) {
jQuery('#' + errorTarget).html(data.responseText).show();
},
success : function(data) {
console.log("in success");
}
};
jQuery(this).ajaxSubmit(options);
return false;
});
The Portlets in Plone are quite handy but I'd like to be able to provide some method to users to be able to temporarily hide/show the portlets column. That is, by clicking a button, the portlets column should collapse and you see the content page in full width. Then clicking again and the portlets panel on the left expands and the main content page width shrinks to accommodate.
I've observed the HTML ID of the portlets column is "portal-column-one" and I tried adding a button to the page that runs javascript to set the visibility property of that element to "hidden" but this seemed to have no effect. I was able to go into Firebug and add style="visibility:hidden;" to the "portal-column-one" element and it had the effect of making the region invisible w/o resizing the page.
I am using Plone 4.1. I have the site configured with navigation portlet on all pages except the main page which has Navigation, Review List and Recent Changes.
So it seems it must be possible to embed some javascript in the page (I was thinking of adding this to the plone.logo page which I've already customized). But I guess its more complicated than the few stabs I've made at it.
Thanks in advance for any advice.
Solution (Thanks to input from Ulrich Schwarz and hvelarde):
The solution I arrived at uses JavaScript to set CSS attributes to show/hide the Portlets Column (Left side) and expand the content column to fill the space the porlets column filled.
I started by customizing the Plone header template to add a link for the user to toggle the view of the Porlets column. I also put the necessary javascript functions in this header.
To customize the header, go to the following page (need to be logged in as Admin of your Plone site):
http://SERVER/SITE/portal_view_customizations/zope.interface.interface-plone.logo
Where:
SERVER is the address and port of your site (e.g. localhost:8080)
SITE is the short name of your Plone Site
To create this page:
Go to Site Setup (as Admin)
Go to Zope Management Interface
Click on "portal_view_customizations"
Click on "plone.logo" (or at least this is where I choose to put the button so it would be located just above the navigation Portlet)
Add the following to the page:
<script>
function getById(id) {
return document.getElementById(id);
}
function TogglePortletsPanel() {
var dispVal = getById('portal-column-one').style.display
if( dispVal == "none") { // Normal display
SetPortletsPanelState("inline");
} else { // Full Screen Content
SetPortletsPanelState("none");
}
}
function SetPortletsPanelState(dispVal) {
var nav = getById('portal-column-one');
var content = getById('portal-column-content');
if( dispVal == "none") { // Normal display
nav.style.display='none';
content.className='cell width-full position-0';
// Set cookie to updated value
setCookie("portletDisplayState","none",365);
} else { // Full Screen Content
nav.style.display='inline';
content.className='cell width-3:4 position-1:4';
// Set cookie to updated value
setCookie("portletDisplayState","inline",365);
}
}
function InitializePortletsPanelState() {
var portletDisplayState=getCookie("portletDisplayState");
//alert("portletDisplayState="+portletDisplayState)
if (portletDisplayState!=null) SetPortletsPanelState(portletDisplayState);
}
function setCookie(c_name,value,exdays) {
//alert(c_name+"="+value);
// cookie format: document.cookie = 'name=value; expires=Thu, 2 Aug 2001 20:47:11 UTC; path=/'
var exdate=new Date();
exdate.setDate(exdate.getDate() + exdays);
var exp= ((exdays==null) ? "" : "; expires="+exdate.toUTCString());
document.cookie=c_name + "=" + escape(value) + exp + "; path=/";
}
function getCookie(c_name) {
var i,x,y,ARRcookies=document.cookie.split(";");
for (i=0;i<ARRcookies.length;i++) {
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==c_name) return unescape(y);
}
}
function addLoadEvent(func) {
var oldonload = window.onload;
if (typeof window.onload != 'function') {
window.onload = func;
} else {
window.onload = function() {
if (oldonload) {oldonload(); }
func();
}
}
}
addLoadEvent(InitializePortletsPanelState);
</script>
<a style="font-size:50%;" href="javascript:TogglePortletsPanel();">Toggle Portlets Panel</a>
6. Save the page
Notes:
I got the names of the plone div elements using Firebug.
I also used Firebug to experiment with different settings to speed up prototyping. For example, editing the HTML inline to verify settings do as expected.
There is a slight but of delay until the Left Portlet panel is hidden. This is only obvious on Safari for me (which is probably due to how fast it is) but not on Firefox or IE.
Maybe it's just a matter of setting the right property: you want display:none, not visibility:hidden.
But even then, the content area will probably not reflow automatically, you'll need to (dynamically) change the class on it as well.
Specifically, you'll need to put classes width-full and position-0 on portal-column-content, instead of width-1:2 and position-1:4.
This must be achieved client side by javascript (jquery).
You must first read documentation about the css grid framework used by plone: deco.gs. The website is down so, git clone this repo: https://github.com/limi/deco.gs and open pages in a webbrowser
Note: you just have to change css classes on the containers.
Try adi.fullscreen, it respects Plone's css-structure as Ulrich Schwarz thoughtfully mentioned.
Im in a page mainpage.aspx" in which i have this code:
<a rel="facebox" href="destination.htm?path=blabla">mainpage</a>
Im using facebox() to load the destination.htm
The problem is in my destination.htm,when im trying to hit an alert(window.location.href) im getting the mainpage.aspx and not the destination.htm.why ? all i want to do is to read the path from the page to get the querystring (but im getting the wrong path).
thanks alot
That is because facebox is not actually redirecting the browser to that page, but fetching via ajax and injecting it into the facebox div on the same page.
You could try grabbing the href attribute of the anchor instead, perhaps something like:
var lastVisited = null;
var lastVisitedQS = null;
$("a[rel='facebox']").facebox()
.click(function() {
lastVisited = this.href;
// you can extract the query string like this
var a = document.createElement('a');
a.href = lastVisited;
lastVisitedQS = a.search; // should give path=blabla
});
and then in your destination.htm do:
alert(lastVisited);
alert(lastVisitedQS);
Hope that helps!
You can use window.location.hostname to get host URL, window.location.pathname to get virtual dir+page and window.location.search to get querystring part of URL
I have a page for an internal app that displays document images streamed from a document storage system using a web service. The problem I am having is that when a user does their search they may get hundreds of hits, which I have to display on one large page so they can print them all. This works fine in Firefox, but in IE it stops loading the images after a while so I get a hundred or so displayed and the rest just have the broken image symbol. Is there a setting somewhere that I can change this timeout?
If the issue is indeed a timeout, you might be able to work around it by using a "lazy load" script and adding new images to the document only after existing images have loaded.
There are a lot of ways to do this, but here's a simple example I threw together and tested. Instead of this:
<img src="image001.jpg" />
<img src="image002.jpg" />
<img src="image003.jpg" />
<img src="image004.jpg" />
<!-- Etc etc etc -->
You could do this:
<div id="imgsGoHere">
</div>
<script type="text/javascript">
function crossBrowserEventAttach(objectRef, eventName, functionRef)
{
try {
objectRef.addEventListener(eventName, functionRef, false);
}
catch(err) {
try {
objectRef.attachEvent("on" + eventName, functionRef);
}
catch(err2) {
// event attachment failed
}
}
}
function addImageToPage()
{
var newImageElement = document.createElement("img");
newImageElement.src = imageArray[nextImageNumber];
var targetElement = document.getElementById("imgsGoHere");
targetElement.appendChild(newImageElement);
nextImageNumber++;
if (nextImageNumber < imageArray.length) {
crossBrowserEventAttach(newImageElement, "load", addImageToPage);
crossBrowserEventAttach(newImageElement, "error", addImageToPage);
}
}
var nextImageNumber = 0;
var imageArray = new Array();
imageArray[imageArray.length] = "image001.jpg";
imageArray[imageArray.length] = "image002.jpg";
imageArray[imageArray.length] = "image003.jpg";
// .
// .
// .
// Snip hundreds of rows
// .
// .
// .
imageArray[imageArray.length] = "image999.jpg";
addImageToPage();
</script>
Each image is added to the page only after the previous image loads (or fails to load). If your browser is timing out, I think that will fix it.
Of course, the problem might actually not be a timeout, but rather that you're running out of memory/system resources and IE is giving up. Or there might be an IE DOM limitation like Sra said.
No final solution, but some hints...
I think the ie Dom hangs up. I,ve seen this in other cases. I needed simply to show the images and used a js which loads the image the time they came into focus, but that want work if you directly hit print I think. Can you use the new css ability to store imagedata directly instead of links. That should solve your problem. I am not quite sure but I think it is supported since ie 7
My guess is that you have to work around the IE setting, the easiest way to do it is simply not showing images that are not loaded or replacing them with a default image:
your html:
<img src="http://domain.com/image.jpg" />
your js:
$('img').load(function(){
// ... loaded
}).error(function(){
// ... not loaded, replace
$(this).attr('src','/whatever/default.jpg');
// ... not loaded, hide
$(this).hide();
});
That is a problem with microsoft. Unfortunately, this is a setting that would have to be changed on every single computer, as there is no remote way to alter it. To change it on your computer, try opening regedit and adding the RecieveTimeout DWORD with a Value of (#of minutes)*6000. Hope this helps-CodeKid1001
Edit: Sorry about that, I forgot to put in the file path:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\InternetSettings
I used something similar to laod HD pictures as a background using ASP Pages
But i used jQuery to handle the images and its loading. This is a sample for 1 image but with a bit of tweaking you can load dynamically
myImage = new Image();
$(myImage).load(function ()
{
$(this).hide(); //Stops the loading effect of large images. can be removed
$('.csBackground li').append(this); //Append image to where you need it
$(myImage).show();
}).attr('src', settings.images[0]) //I pass an array from ASP code behind so 0 can be 'i'
.error( function { checkImages(); } ) //try and relaod the image or something?
So instead of changing the timeout- just try and reload the images on error.
Otherwise i only found a solution that is client specific (HTTP Timeout)
http://support.microsoft.com/kb/813827
I'm generating a file on the server which takes some time. For this, I have a hidden iframe which I then set the .src attribute to an aspx file i.e iframe.src = "/downloadFile.aspx"
While this is taking place, I'd like to have a call to a web service return the progress. To do this, I thought I could use window.setInterval or window.setTimeout but Javascript seems to be blocked as soon as I set the iframe src attribute.
Does anyone know how to get around this or perhaps try a different approach?
I have also tried handlers, but the request never gets to the server so I'm assuming is a browser/javascript issue.
//Function that gets the file
function GetFile() {
setTimeout(GetProgress, 1000);
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.src = "downloadFile.aspx";
}
/*DOES NOT WORK - Function that gets progress posts data but never recieves it until the iframe has finished and presents the file download dialog to the user. */
function GetProgress() {
$.post('progress.file', function(data) {
console.log(data);
setTimeout(GetProgress, 1000);
});
}
Not sure exactly what you doing, but try putting your iframe.src=... code in window.setTimeout as well.