• Login
  • login to access premium content (it's free).

Responsive Images for Every Browser on the Planet

my contribution to the conversation

its basically the <picture> tag for older browsers

See Demo

Resize your browser, rotate your phone to see the various images load as the environment changes.

The new HTML5 picture tag requires us to abandon older browsers to get responsive images. But all of the pieces are in place already for those browsers. Here is how to do it.

This approach uses the existing tag with the src attribute specially designed for the browser to ignore.

<img src="/0#/path/to/image.jpg">

This approach requires a 1 pixel gif on the server at the root level named 0.

Here is what we get with our responsive images

Similar syntax to the existing <img> tag Serves the correct image to Mobile, Desktop, and Retina for landscape or portrait. Uses a wrapper that allows for Mobile/Desktop styling similar to the way bootstrap does it. For the most part this does not rely on any type of browser sniffing*. * This demo sniffs for firefox broswers and uses the /0# syntax and also sniffs for IE9 and comments out the proprietary filters that are only for IE678.

All of the images on this page are generated (server side) from the same image. The correct image is served up based on the Screen size, Retina capabilities, and device orientation.

The markup for each images is as follows M100 would indicate 100% on mobile, D70 indicates 70% on Desktop.

Notice the strange src in the <img> tag. The strange syntax is designed so that the client never makes a request for the source. Then the src is chosen with a css selector to determine the actual src depending on the device.

 ...
<div class="image center M100 D70">
 <img src="/0#/apps/test/test.jpg">
</div>
...
<div class="image right M50 D30">
 <img src="/0#/apps/test/test.jpg">
</div>
...
<div class="image left M70 D30">
 <img src="/0#/apps/test/test.jpg">
</div>
...

The path to the image can be a real path or a meta path to the image on the server. The src path is used in the CSS declaration to actually decide what images will be delivered based on screen size, orientation and retina capabilities.

The syntax allows for delivery of the correct content and hinted display for mobile or desktop. It could easily be extended for additional breakpoints

Art Direction means it will send the correct image server for portrait and landscape devices. Consequently all the problems the picture element was created for are solved with this approach. If you look at the syntax for the picture element its syntax relies heavily on CSS. The system I am proposing actually just uses CSS with inline <style> tags for defining the context of the images.

The images are actually loaded as background images and using an IE filter for IE678.

Supported Browsers. (haven't found one where it doesn't work)

  • Internet Explorer - All versions from 6 on up (versions 6,7,8 require respond.js and also for 6 it requires Dean Edwards IE7.js)
  • Chrome - All versions
  • Safari - All Versions
  • Opera - All versions
  • Firefox - All versions from 4 and up
  • Mobile Safari - All versions
  • Android - All Versions

The <style> tag looks like this for each image.

  <style>

     /* IE 6,7,8 Image                 */
     img[src*="/apps/test/test.jpg"] {
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/apps/test/test.jpg?for=ie678',sizingMethod='scale');
      -ms-filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='/apps/test/test.jpg?for=ie678',sizingMethod='scale')";
      padding-bottom:56.25%;
     }

     /* Mobile Image Portrait          */ @media screen and (orientation:portrait) and (max-width: 980px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=mobile-portrait');
      padding-bottom:100%;
     }}

     /* Mobile Image Landscape         */ @media screen and (orientation:landscape) and (max-width: 980px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=mobile-landscape');
      padding-bottom:56.25%;
     }}




     /* Retina Mobile Image Portrait   */ @media screen and (orientation:portrait) and (-webkit-min-device-pixel-ratio: 2), only screen and (orientation:portrait) and (min-resolution: 192dpi) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/XPATH/apps/test/test.jpg?for=mobile-retina-portrait');
      padding-bottom:100%;
     }}

     /* Retina Mobile Image Landscape  */ @media screen and (orientation:landscape) and (-webkit-min-device-pixel-ratio: 2), only screen and (orientation:landscape) and (min-resolution: 192dpi) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=mobile-retina-landscape');
      padding-bottom:56.25%;
     }}




     /* Desktop Image Portrait         */ @media screen and (orientation:portrait) and (min-width: 981px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=desktop-portrait');
      padding-bottom:100%;
     }}

     /* Desktop Image Landscape        */ @media screen and (orientation:landscape) and (min-width: 981px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=desktop-landscape');
      padding-bottom:56.25%;
     }}




     /* Retina Desktop Image Portrait  */
     @media screen and (orientation:portrait) and (-webkit-min-device-pixel-ratio: 2) and (min-width: 981px),screen and (orientation:portrait) and (min-resolution: 192dpi) and (min-width: 981px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=desktop-retina-portrait');
      padding-bottom:100%;
     }}

     /* Retina Desktop Image Landscape */ @media screen and (orientation:landscape) and (-webkit-min-device-pixel-ratio: 2) and (min-width: 981px),screen and (orientation:landscape) and (min-resolution: 192dpi) and (min-width: 981px) {
     img[src*="/apps/test/test.jpg"] {
      background-image:url('/apps/test/test.jpg?for=desktop-retina-landscape');
      padding-bottom:56.25%;
     }}

  </style>

Reference this Stackoverflow Question regarding the img src syntax.

dynamically loaded javascript

I was starting to get a lot of jQuery plugins on WebBlocks.co the CMS that runs learnhtmlos.com. They were all needed but only in certain contexts. I found these 3 articles useful. I rolled them into my own safe code execution script.

Start by specifying a global Javascript variable like jQuery.colorbox and its url and the code you want to run. If the url is already loaded it will execute the code, if not it will load url then execute the code.

The ridiculous case of adding a script element

Check if a jQuery plugin is loaded

Dynamically load js inside js

The script

runSafe=function(name,url,callback){
 if(typeof window[name] != 'undefined'){
  callback;
 } else {
  var js = document.createElement('script');
  js.src = url;
  js.onload = js.onreadystatechange = callback;
  var first = document.getElementsByTagName('script')[0];
  first.parentNode.insertBefore(js, first);
 }
}

Usage

runSafe('jQuery.colorbox', '/apps/scripts/colorbox.min.js',function(){

 // do something with the colorbox plugin

});

UTF and HTML/OS

I decided to roll my own UTF-8 string compatability.

So if you have a string of text that you know is UTF-8 encoded you can get its length and also parse it just like regular text.

Step 1.Step 2.Step 3.
Prepare:
temp=asUTF(text)
Perform string manipulations using the alternative tags length_(temp), middle_(temp) etc (notice the underscore)...Convert back to string
text=asText(temp)

For Example this page...

<!doctype html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title>UTF8 Demo</title>
 </head>
 <body>
  <<
   msg="Everybody is using emoticons (😊😋😨😆) These days"
   display "msg="+msg+'<br>' /display
   display "Length(msg)="+length(msg)+'<br>' /display
   msg2=asUTF(msg)
   display "Length_(msg2)="+length_(msg2)+'<br>' /display
   msg_middle=middle_(msg2,31,31)
   display '31st character is "'+asText(msg_middle)+'"' /display
  >>
 </body>
</html>

Would display this output...

msg=Everybody is using emoticons (😊😋😨😆) These days
Length(msg)=58
Length_(msg2)=46
31st character is "😊"

Here are the Functions

function asUTF(text) locals x,s,st,i,a do
 x=1
 st='ERROR'
 while x<length(text)+1 do
  i=getascii(middle(text,x,x))
    if i<192 then s=1
  elif i<224 then s=2
  elif i<240 then s=3
  elif i<248 then s=4
  elif i<252 then s=5
  elif i<254 then s=6
  /if
  a='ERROR'
  a=middle(text,x,x+(s-1))
  if st='ERROR' then st=a else st=merge(st,a) /if
  x=x+s
 /while
 return st /return
/function


function middle_(text_UTF,s,e) do
 return gettable(text_UTF,s,e,1,1) /return
/function


function length_(text_UTF) do
 return cols(text_UTF) /return
/function


function left_(text_UTF,i) do
 return gettable(text_UTF,1,i,1,1) /return
/function


function right_(text_UTF,i) locals l do
 l=cols(text_utf)
 return gettable(text_UTF,l-i+1,l,1,1) /return
/function


function reverse_(text_UTF,i) do
 return reversecols(text_UTF) /return
/function


function concat_(text1_UTF,text2_UTF) do
 return merge(text1_UTF,text2_UTF) /return
/function



function asText(text_UTF) locals text,x do
 text=''
 for name=x value=1 to cols(text_UTF) do
  text=text+text_UTF[x,1]
 /for
 return text /return
/function

Semicolon URL notation with HTML/OS

Sometimes it is desirable to use the semicolon notation to reuse overlay's. Instead of duplicate overlays we can instead opt to use semicolon notation.

This allows you to reference a single overlay from multiple pages.

The problem

The main issue with doing it this way is the "page" value gets lost.

index.html

<!doctype html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title></title>
 </head>
 <body>
  <a href="master.overlay;doit">Run doit Overlay</a><hr>
 </body>
</html>

master.overlay

<<goto error(400)>>
<<overlay doit
 goto page
>>

The Solution

To overcome that you can use the following coding pattern.

index.html

<<
expand file="/system/clearimage/DLL.lib" /expand
>>
<!doctype html>
<html lang="en">
 <head>
  <meta charset="utf-8">
  <title></title>
 </head>
 <body>
  <a href="<<page>>">RELOAD</a><br>
  <a href="<<pagelink('master.overlay;doit','qwerty','Terry')>>">doit</a><hr>
  <<page>><br>
  <<page_>><br>
  <<page_()>><br>
  <<pagefromdoit>><br>
  <<qwerty>><<qwerty='ERROR'>>
 </body>
</html>

master.overlay

<<goto error(400)
 # 
 # This is a pattern for master overlay's 
 # 
 # To call a master overlay use the following syntax
 #
 # <a href="<<pagelink1('master.overlay;doit')>>">Run Do It</a>
 # <a href="<<pagelink('master.overlay;doit',varname,varvalue)>>">Run Do It</a>
 #
 # execute get_() to initialize each overlay
 # 
 # Use page_ in place of page for goto's
 # tag can also be used for goto's
 # 
 # notice the goto error(400) that will be invoked if you forget and do a goto page
/#
>>

<<overlay doit get_()

 pagefromdoit='Welcome: '+qwerty+' '+random(1000,9999)+' '+page_+' '+page+' '+page_()+' '+tag
 goto page_

>>

Thats it for now.

LAYOUT()

A new way to use layout

The layout tag is usually used to take an Aestiva table and create some html markup with it. For example

 mystuff
+-----------------------+
| Dog   | Cat   | Mouse |
|-------+-------+-------|
| Blue  | Green | Red   |
+-----------------------+

Could be displayed as a table like this using layout()

<table>
 <<layout(mystuff,'<tr><td>',[1],'</td><td>',[2],'</td><td>',[3],'</td></tr>')>>
</table>
DogCatMouse
BlueGreenRed

But...

There is another very useful way to use layout

It can be used to extract columns from a variable and place them into a new variable. For example using the variable mystuff we could extract the third and the first column in that order using layout.

newvar=layout(mystuff,[3],[1])

Which would yield newvar...

 newvar
+---------------+
| Mouse | Dog   |
|-------+-------|
| Red   | Blue  |
+---------------+