Recently I discussed at length about Responsive Images and Responsive Image as Service.
In this post I will discuss how we can use some effective templating techniques to implement highly maintainable responsive image markup.
Templates express presentation logic of a web application. Templates are quite similar to plain HTML markup but they are re-usable and modular. Most template languages have provision for variables, tags, filters, and inheritance.
In a template, variables are used to render the data in the markup. Tags control logic of the template. Filters modify the variables for display. Inheritance allows child template to inherit base template skeleton and override it.
Django Template
Django is a Model-View-Controller (MVC) framework, or more precisely a Model-Template-View (MTV) framework. In Django , MVC Controller is Django View and MVC View is Django Template. A Django Template renders data passed by Django View.
I will demonstrate use of Django templates to implement responsive image markup. Although this demonstration relies on Django template language other template languages can easily support this kind of approach.
Special markup using Django Template
Following template main.html
is looping over template variable images
(a list of image
objects). This variable images
was passed by a Django View function to template main.html
. Inside the loop main.html
is using a custom Django inclusion tag[1] show_responsive_image
which basically calls another template responsive-image.html
and passes image
object. Please note each image
object has named attributes which can be accessed when required.
<!-- main.html Template-->
<html>
<header>
<script src="/static/js/picturefill.js"></script>
</header>
<body>
{{ "{% for image in images " }} %}
{{ "{% show_responsive_image image " }} %}
{{ "{% endfor " }} %}
</body>
</html>
<!-- responsive-image.html v1 Template-->
<img src="{{ "{{ MEDIA_URL " }}}}{{ "{{image.path " }}}}" alt="{{ "{{image.title" }}}}">
At the moment responsive-image.html
is just rendering normal img
elements.
<!-- responsive-image.html v1 Template-->
<img src="/static/images/image1.jpg" alt="Profile-1">
<!-- responsive-image.html v1 Template-->
<img src="/static/images/image2.jpg" alt="Profile-2">
This could have been done simply in main.html
so why another template?
Picturefill
Let's try again, with responsive version of responsive-image.html
based on Picturefill[2].
<!-- responsive-image. v2 Template done with Picturefill-->
<span data-picture data-alt="{{ "{{image.title" }}}}">
<span data-src="{{ "{{ MEDIA_URL " }}}}small/{{ "{{image.path " }}}}"></span>
<span data-src="{{ "{{ MEDIA_URL " }}}}medium/{{ "{{image.path " }}}}" data-media="(min-width: 400px)"></span>
<span data-src="{{ "{{ MEDIA_URL " }}}}large/{{ "{{image.path " }}}}" data-media="(min-width: 800px)"></span>
<span data-src="{{ "{{ MEDIA_URL " }}}}extralarge/{{ "{{image.path " }}}}" data-media="(min-width: 1000px)"></span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="{{ "{{ MEDIA_URL " }}}}medium/{{ "{{image.path " }}}}" alt="{{ "{{image.title" }}}}">
</noscript>
</span>
This new template will generate following markup source required by Picturefill[2:1] to provide responsive images.
<!-- responsive-image. v2 Template done with Picturefill-->
<span data-picture data-alt="Profile-1">
<span data-src="/static/images/small/image1.jpg"></span>
<span data-src="/static/images/medium/image1.jpg" data-media="(min-width: 400px)"></span>
<span data-src="/static/images/large/image1.jpg" data-media="(min-width: 800px)"></span>
<span data-src="/static/images/extralarge/image1.jpg" data-media="(min-width: 1000px)"></span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="/static/images/medium/image1.jpg" alt="Profile-1">
</noscript>
</span>
<!-- responsive-image. v2 Template done with Picturefill-->
<span data-picture data-alt="Profile-2">
<span data-src="/static/images/small/image2.jpg"></span>
<span data-src="/static/images/medium/image2.jpg" data-media="(min-width: 400px)"></span>
<span data-src="/static/images/large/image2.jpg" data-media="(min-width: 800px)"></span>
<span data-src="/static/images/extralarge/image2.jpg" data-media="(min-width: 1000px)"></span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="/static/images/medium/image2.jpg" alt="Profile-2">
</noscript>
</span>
Again actual magic or DOM manipulation will happen onload
by client-side JavaScript using Picturefill.js
that is included in our markup header. Assuming if you are using a desktop machine, final DOM markup will look like (inserted img
),
<!-- responsive-image. v2 Template done with Picturefill-->
<span data-picture data-alt="Profile-1">
<span data-src="/static/images/small/image1.jpg"></span>
<span data-src="/static/images/medium/image1.jpg" data-media="(min-width: 400px)"></span>
<span data-src="/static/images/large/image1.jpg" data-media="(min-width: 800px)"></span>
<span data-src="/static/images/extralarge/image1.jpg" data-media="(min-width: 1000px)">
<img src="/static/images/extralarge/image1.jpg" alt="Profile-1">
</span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="/static/images/medium/image1.jpg" alt="Profile-1">
</noscript>
</span>
<!-- responsive-image. v2 Template done with Picturefill-->
<span data-picture data-alt="Profile-2">
<span data-src="/static/images/small/image2.jpg"></span>
<span data-src="/static/images/medium/image2.jpg" data-media="(min-width: 400px)"></span>
<span data-src="/static/images/large/image2.jpg" data-media="(min-width: 800px)"></span>
<span data-src="/static/images/extralarge/image2.jpg" data-media="(min-width: 1000px)">
<img src="/static/images/extralarge/image1.jpg" alt="Profile-1">
</span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="/static/images/medium/image2.jpg" alt="Profile-2">
</noscript>
</span>
Imager.js
Let say after sometime you decided to replace Picturefill[2:2] with Imager.js[3]. This requires following changes,
- User
imager.js
in header instead ofpicturefill.js
- Add JavaScript code to interpolate width to a string
- Update the responsive image tag for our template
<!-- main.html Template-->
<html>
<header>
<script src="/static/js/imager.js"></script>
<!-- Interpolate {width} to a string -->
<script>
new Imager({
availableWidths: {
200: 'small',
320: 'medium',
640: 'large',
1024: 'extralarge'
}
});
</script>
</header>
<body>
{{ "{% for image in images " }} %}
{{ "{% show_responsive_image image " }} %}
{{ "{% endfor " }} %}
</body>
</html>
<!-- responsive-image. v3 Template done with Imager.js -->
<div data-src="{{ "{{ MEDIA_URL " }}}}{width}/{{ "{{image.path " }}}}" data-alt="{{ "{{image.title" }}}}"></div>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="{{ "{{ MEDIA_URL " }}}}medium/{{ "{{image.path " }}}}" alt="{{ "{{image.title" }}}}">
</noscript>
After above three changes and assuming if you are using a desktop machine, final DOM markup will look like,
<img src="/static/images/large/image1.jpg" data-src="/static/images/{width}/image1.jpg" class="image-replace" alt="Profile-1">
<img src="/static/images/large/image2.jpg" data-src="/static/images/{width}/image2.jpg" class="image-replace" alt="Profile-2">
Final Words
Here I briefly covered, some clever use of Django template language to write highly maintainable responsive image markup. Although I have not tried this approach with other template languages, I am quite sure this will work with others as well.