Javascript replace( ) function running more than once on the same image

Go To StackoverFlow.com

1

I'm running a Javascript replace function to replace standard images with class="replace-2x"on my jQuery Mobile site with Retina-quality images if the user is on a mobile device with Retina display. For example, on a Retina device, logo.png will be replaced with logo@2x.png. The JS function is here:

function highdpi_init() {
    $(".replace-2x").each(function () {
        var src = $(this).attr("src");
        $(this).attr("src", src.replace(".png", "@2x.png").replace(".jpg", "@2x.jpg"));

    });
}

$(".page").live('pageinit',function(event){
    highdpi_init();
});

I'm now running into an issue where the replace function is running more than once. So for example, it replaces logo.png with logo@2x.png as the page is loading, but then as the page continues to load, it KEEPS replacing .png with @2x.png in the img src over and over so that the image tag ends up looking like this:

<img src="mobile/images/logo@2x@2x@2x@2x@2x@2x@2x@2x@2x@2x@2x.png" class="replace-2x" alt="logo" width="200">

How can I prevent this from replacing on a single img element more than once? Keep in mind, I will have multiple images on the same page, so the function will need to apply to all images, but only one time each.

2012-04-05 15:51
by adamdehaven
The pageinit event appears to be firing more than once. How about using .one('pageinit') instead of .live('pageinit') - Matt Ball 2012-04-05 15:53
Will this allow the function to still run for all the images on the page? When I used .one the function doesn't seem to fire.. - adamdehaven 2012-04-05 15:55
Not if new images were added after the first time the 'pageinit' event was fire - Juan Mendes 2012-04-05 15:58
Can you use the .one on the function itself rather than taking out the .live ? Or is there another way - adamdehaven 2012-04-05 15:59
How do you determine if the mobile has a retina display? I would consider checking when you render the page and setting the correct file path then. Doesn't seem like you need to mess around with javascript for something like thi - musefan 2012-04-05 16:02
I define the replace-2x class in a special stylesheet that is only loaded if the device is high-pixel-density compatable (pixel ratio of 2)

<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" href="/css/highdpi.css"/>adamdehaven 2012-04-05 16:06

@adamdehaven You could improve your code and remove the JS by making your images use background images. Then your stylesheet could determine what to use as the background-image: url() propert - Juan Mendes 2012-04-05 16:09
Question: in the JS code listed above, on the line $(".replace-2x").each(function () { how could I modify this to say, "If it has the class, and .css('font-size') == "1px" - adamdehaven 2012-04-05 16:13
You could add a check inside of the handler itself function(){if ($(this).css('font-size') == '1px') {return}}Juan Mendes 2012-04-05 16:15
So where would I place this in the code - adamdehaven 2012-04-05 16:17
As the first line in your function that gets passed to $('.replace-2x' ).each(function(){ // check goes here})Juan Mendes 2012-04-05 16:18


3

The problem is surely that your 'pageinit' event is being called more than once. You can either follow MДΓΓ БДLL's idea (which won't work if images are dynamically added) or you can make your handler smarter so that it doesn't replace the src if it already was replaced

function highdpi_init() {
    $(".replace-2x").each(function () {
        var $this = $(this);
        var src = $this.attr("src");
        $this.attr("src", src.replace(".png", "@2x.png").replace(".jpg", "@2x.jpg"));
        // Remove the class so it doesn't replace it again
        $this.removeClass('replace-2x')

    });
}

You don't need JS for this, you could do it in CSS only.

<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" href="/css/highdpi.css"/> 

You could make your images look like

<img src="transparent.gif" class="logo-a" alt="logo" width="200" />

And in highdpi.css you could do

img.logo-a {
   background-image: url('file@2x.png') 
}

And in lowdpi.css

img.logo-a {
   background-image: url('file.png') 
}
2012-04-05 15:59
by Juan Mendes
Thanks!!! This works perfectly! - adamdehaven 2012-04-05 16:03
Question: in the JS code listed above, on the line $(".replace-2x").each(function () { how could I modify this to say, "If it has the class, and the class declares that .css('font-size') == "1px" - adamdehaven 2012-04-05 16:14
@adamdehaven Did you just ask the same question twice? I answered it up there, be sure to mark the question as answered. And also be sure to test the solution I posted that doesn't require JS, just CS - Juan Mendes 2012-04-05 16:16
I appreciate the CSS-only solution, but I don't want to have to code everything twice. I want to be able to use JS so that I can include the image on the page once without having to modify a separate css file every time I insert an image onto the sit - adamdehaven 2012-04-05 16:22


0

Using .one() should work since it is just a binding and if you are using Jquery Mobile the way that is suggested it will be just fine. That is unless you are passing back the html from the server. In which case it would be a good idea to add an extra condition to make sure that the src doesn't already have @2x.png before replacing.

2012-04-05 16:03
by Brandon Kindred


0

There is disappointingly little documentation on pageinit on the offical jQuery Mobile docs. So I'm going to speculate here. It looks like pageinit is used to fire events for when a specific DOM element has finished loading, since it may not have been loaded on the initial page load (deferred until needed). That being said, it may be that adding/altering images to the DOM element in question fires the pageinit again. Could you tag each updated image with something that says, 'hey, I've already been updated to 2x'? Something such as

$.data(targetimg, 'retinafied', true); 

And then check for that value before replacing the src?

2012-04-05 16:06
by Matt
This adds a new state variable, just removing the .replace=2x is simple - Juan Mendes 2012-04-05 16:08
Ads