Getting off the table, viewport,
devicewidth, code-behind and the lifecycle of an ASP page.
Until the nasty gram from Google about
de-ranking a clients pages for not properly supporting mobile
devices, I was plenty happy. I thought the pages looked fine, and
actually adding the viewport header made them look worse on mobile
devices. Can't fight google.
I had heard grumblings about tables and
how pedestrian they are, but getting off them, it was like losing
Flash again. Maybe that is a bit dramatic, but I have learned to
coexist with HTML 5 and my Action Script code still has a home, but
tables. They really had to go.
Life is simply better without them, F
all the matching <tr and </td, good riddance. Tables will live
on with html marketing emails, where divs will ruin your day, but divs
just blend and they allow for the dynamic wrapping and sizing of the
target device for a more robust user experience across devices.
This opened a new can of worms; making
device width usable while your page elements are created. Most static pages can be resolved via CSS, style="width:100%; max-width:640px;" Yet for complex dynamic pages, you need to know the device capabilities when the page is created serverside. As an example check out http://aqua8472.com/seeitwork.aspx The grid elements are created dynamically and based on the device serverside. In the ASP
lifecycle, all server side events process before the page is
rendered. That makes sense, the page is essentially static and has
no contract back to the server, ajax muddies this, but for all
practical purposes, once the server is done with its back end
processing, the HTML is rendered and the server has moved on. Now I
was facing a chicken and egg scenario, I want to size the elements on
my page server-side based on the device. Problem...
To solve this touches on several areas
within the page lifecycle. Essentially, I fed my device width via a
webservice call and created a session variable with the width for use
during server-side processing of the page. Customizing IHttpModule
with an event for PostAcquireRequestState
allows for a redirect to a detection
page, this
detection page via a webservice call creates the session variable.
Keep in mind this is only available to asp pages.
privatevoidOnPostAcquireRequestState(objecto,EventArgsargs)
{
<<snippet>>
HttpApplicationapp=(HttpApplication)o;
BrowserSupport.BrowserInfobi=newBrowserSupport.BrowserInfo(app.Request);
if(bi.mobile)
{
if(app.Session["deviceWidth"]!=null)
ProcessASPX(app,newBrowserSupport.BrowserInfo(app.Request));
else
{
if(!sPageRequested.ToLower().Equals("detectdevice.aspx"))
app.Response.Redirect("/noc/detectdevice.aspx?r="+app.Request.Path,true);
}
}
else
ProcessASPX(app,newBrowserSupport.BrowserInfo(app.Request));
Javascript
handles the detection of the device width and calls a webservice.
Keep in mind, you have created a race condition and you need to wait
for the webservice call to complete before redirecting to the
original page passed in as a url param from OnPostAcquireState
<metaname="robots"content="noindex,nofollow"/>
<metaname="viewport"content="width=device-width,initial-scale=1"/>
<scripttype="text/javascript"lang="javascript"src="/scripts/jquery.js"></script>
<scripttype="text/javascript"lang="javascript">
$(document).ready(function(){
varwindowWidth=(window.innerWidth>0)?window.innerWidth:screen.width;
jdwebservice.setdevicewidth(windowWidth,
setDeviceWidthOnSuccess,setDeviceWidthOnFail);
});
functionsetDeviceWidthOnSuccess(result){
if(result){
varurlp=location.search;
varoffset=urlp.indexOf('?r=');
if(offset<0){
alert('Unabletolocateurlparam:'+urlp);
}
else
{
window.location=urlp.substring(offset+3);
}
}
else
alert('Wearehavingtroublecommunicatingwithourbackendservers.Contactsupportwithmessagedetectdevice.setDeviceWidth.(result):'+result.get_message());
}
functionsetDeviceWidthOnFail(result){
alert('Wearehavingtroublecommunicatingwithourbackendservers.Contactsupportwithmessagedetectdevice.setDeviceWidth.Tryreloadingthispage.'+result.get_message());
}
</script>
In the
detection page, we want to keep the crawlers happy by notifying them
this is a temp 302 redirect and not a 200 success. Response codes
can only be set by the server.
protectedvoidPage_Load(objectsender,EventArgse)
{
Response.Status="302DeviceDetectionRedirect";
Response.StatusCode=302;
}
The webservice is a simple call
and looks like this.
[WebMethod(EnableSession=true)]
publicboolsetdevicewidth(uintdeviceWidth)
{
boolbResult=false;
try
{
if(Session["deviceWidth"]==null)
Session.Add("deviceWidth",deviceWidth.ToString());
else
Session["deviceWidth"]=deviceWidth.ToString();
bResult=true;
}
catch(ExceptionE)
{
Utility.SendEventLogError("("+HttpContext.Current.Request.UserHostAddress+")
jdWebservices:setdevicewidth,Exception:"+E.Message);
}
returnbResult;
}
Now on a page load, for the
parent div I set the width at runat server, then on its oninit, use
the session device width as below:
<divclass="divIndexHeader"id="ixHeaderDiv"oninit="Div_Init"runat="server">
protectedvoidDiv_Init(objectsender,EventArgse)
{
<snippet>
notice we only mess with mobile devices, desktop are as designed.
BrowserSupport.BrowserInfobi=newBrowserSupport.BrowserInfo(Request);
if(bi.mobile)
{
if(Session["deviceWidth"]!=null)
{
uintuWindowWidth=0;
if(!uint.TryParse((string)Session["deviceWidth"],outuWindowWidth))
thrownewException("UnabletoconvertSession[\"deviceWidth\"]
toanuint,"+ (string)Session["deviceWidth"]);
if(uWindowWidth<975)
((HtmlGenericControl)sender).Style["Width"]=uWindowWidth+"px";
}
else
thrownewException("Session[\"deviceWidth\"]isnull,
wasdetectdevicerunfirst?");
}
}
For embedded elements with width:X%, they all fall in line based on the parent element width. Having the power of server-side processing, allows for creativity to adjust/remove padding between inline-block or float divs.
Getting creative, for our low
resolution devices, like the iPhone, size your images to take the
width of the device, then adjust the padding for higher resolution
devices support based on your original layout.
<divid="divTour4"class="divIndexTour"runat="server"oninit="divTour_Init">
if(Session["deviceWidth"]!=null)
{
uintuWindowWidth=0;
if(!uint.TryParse((string)Session["deviceWidth"],outuWindowWidth))
thrownewException("UnabletoconvertSession...
if(uWindowWidth<375)
{
((HtmlGenericControl)sender).Style["Width"]=uWindowWidth+"px";
((HtmlGenericControl)sender).Style["padding-left"]="0px";
}
elseif((uWindowWidth<900)&&(uWindowWidth>376))
{
((HtmlGenericControl)sender).Style["Width"]=(uWindowWidth*.75)+"px";
((HtmlGenericControl)sender).Style["padding-
left"]=(uWindowWidth*.12)+"px";
}
elseif((uWindowWidth<975)&&(uWindowWidth>901))
{
uintuPadding=75-((975-uWindowWidth)/2);
((HtmlGenericControl)sender).Style["padding-left"]=uPadding+"px";
}