locked
How to force a responsive design to "Desktop" viewport mode RRS feed

  • Question

  • User1692409842 posted

    I have a responsive design site using CSS media queries to make my site look great on any device. Today, I was playing with the new mobile views as part of the VS 2012.2 update. After learning about the new mobile routes feature, cool stuff, but... the thought of duplicating all kinds of master pages and .aspx pages (it's a Web Forms app) started to get real ugly real fast. Loving the friendly URLs though!

    I realized all I really needed to do was somehow make the mobile browser ignore the meta viewport tag either on load or on the fly after a load. After coding up some javascript to try it on the fly, I found out that at least with iOs and some Android browsers, modifying or deleting the viewport meta tag on the fly like that is ignored. The tag is getting added/deleted... but the browser totally ignores that change and no reflow occurs. So that shot that idea in the head. Testing on IE9 on my Windows Phone 7 revealed the same behavior. Rats.

    I am pondering some other ideas, like setting a cookie value, that way the user's preference for responsive vs. full Desktop will be remembered on all the various pages on the site. Or use an anonymous ASP.Net profile, which I beleve uses cookies with a fallback to querystrings, or something along those lines. Then on the server side I can render the tag as needed, easy.

    I wish there was some way to just force a reflow without a reload at a different viewport setting on the fly in the popular mobile browsers. Is there? MOzilla has nsIDOMWindowUtils.setCSSViewport() but I have no idea if that works outside of the Mozilla universe...

    I'd like a solution that doesn't require some kludgy duplication of 99% of my master/content pages except for a single tag. Preferably some way to load the same page with different viewport settings that I can detect on the server site... I can't count on using a querystring since various pages on the site are already using querystrings. Or perhaps some kind of scheme to append a "desktop=true" to the end of every possible querystring if the user clicks the "Desktop view" button, perhaps an HTTP module? Then I can check that in the masterpage and emit the correct meta tag as needed.

    If I was just starting from scratch I'd consider having ".mobile" this and that versions of pages, but it's too late I'm not going to go back and create all new mobile pages, which seems really anti-DRY when all we're talking about is one freakin' tag difference between the views. Perhaps some clever scheme of nested master pages? Hm.

    Any ideas? This problem has to have been sovled before, but searching around didn't reveal any particularly clever and simple answer...

    Tuesday, March 5, 2013 8:11 PM

Answers

  • User1692409842 posted

    Thanks very much for the runat="server" idea. That got me thinking and I came up with what I think is a good solution. My requirements are:

    1. To show a Desktop/Mobile button at the bottom of every page to easily enabled switching back and forth. Note that in my case when I say "mobile" I really mean "enable the meta viewport tag".
    2. The button does not show unless the screen is below a certain size.
    3. Once the button is clicked, it will always show, so you can get back to the mobile (responsive) view no matter what.
    4. Once the button is clicked, it will remember the user preference for all other pages on the site.

    To do this, I set up my meta tag in my master page as follows:

    <meta runat="server" id="viewporttag" name="viewport" content="width=device-width, initial-scale=1.0" />

    Here is my button at the bottom of the page:

    <asp:Button ID="Desktop_button" runat="server" Text="Desktop View" OnClick="Desktop_click" />

    Then in my .css file, the button is hidden by default, unless its CSS class is set to ".clicked", and is set to always show at small screen sizes:

    #Desktop_button
    {
    	margin: 0 0 0 5%;
    	display: none;
    }
    
    	#Desktop_button.clicked
    	{
    		display: inline;
    	}
    }
    
    @media only screen and (max-width: 850px)
    {
    	#Desktop_button
    	{
    		display: inline;
    	}
    }

    Now for the fun part, in my masterpage codebehind:

    protected void Desktop_click(object sender, EventArgs e)
    {
    	//if it's never been set or has been set to Mobile:
    	if (Session["view"] == null || Session["view"].ToString() == "Mobile")
    	{
    		viewporttag.Visible = false; //remove meta viewport tag
    		Session["View"] = "Desktop"; //we are now in desktop view
    		Desktop_button.Text = "Mobile View";
    	}
    	else
    	{
    		viewporttag.Visible = true; //enable meta viewport tag for mobile views
    		Session["View"] = "Mobile";
    		Desktop_button.Text = "Desktop View";
    	}
            addClass(Desktop_button, "clicked");
    }
    protected void Page_Load(object sender, EventArgs e) { //set up defaults based on current view setting. This is called before button click event! if (Session["View"] != null) //if the button has ever been clicked, View won't be null addClass(Desktop_button, "clicked"); //always show the button if it has been clicked, ever if (Session["View"] != null && Session["View"].ToString() == "Desktop") //was it explicitly set? viewporttag.Visible = false; //remove meta viewport tag //now set default text on buttons for freshly loaded pages if (Session["View"] == null || Session["View"].ToString() == "Mobile") Desktop_button.Text = "Desktop View"; else Desktop_button.Text = "Mobile View"; }

    private void addClass(WebControl mycontrol, string cssclass)
    {
        if (mycontrol.CssClass.Split(' ').Contains(cssclass))
        return;
        if (!string.IsNullOrEmpty(mycontrol.CssClass))
         mycontrol.CssClass += " "; //add separator
        mycontrol.CssClass += cssclass;
    }

    This scheme seems to be working great so far. I'm still testing this but I'd love to hear some feedback or if anyone finds this useful.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, March 7, 2013 9:07 AM

All replies

  • User1943143334 posted

    Hi,

    If i understood you question correctly, you wish to remove the Viewport meta tag on Page Load! Is that correct?

    If yes, then why don't you remove it completely from the Project!

    Else,

    You can also do that in Server Side, by adding runat="server" attribute to it and with id, you can access it. 

    <meta name="viewport" id="viewportid" runat="server"  content="width=device-width" />
    
    //Code behind
    
    Protected void Page_Load(object sender, EventArgs e)
    {
      viewportid.Attributes.Remove("content");
    }

    Hope it helps u...

    Wednesday, March 6, 2013 10:36 AM
  • User1692409842 posted

    Sorry my question was missing one vital fact, what I want to do is have my normal responsive site load by default for all browsers, but if a user clicks a "Desktop" button at the bottom of the page (too see a "wider" view of the site, such as you'd see on a desktop monitor) I want to be able to do that on the fly. Since I can't force a reflow on the fly, then by reloading the page but ignoring the meta viewport tag altogether, but only after that button is clicked.

    Wednesday, March 6, 2013 4:14 PM
  • User1692409842 posted

    Thanks very much for the runat="server" idea. That got me thinking and I came up with what I think is a good solution. My requirements are:

    1. To show a Desktop/Mobile button at the bottom of every page to easily enabled switching back and forth. Note that in my case when I say "mobile" I really mean "enable the meta viewport tag".
    2. The button does not show unless the screen is below a certain size.
    3. Once the button is clicked, it will always show, so you can get back to the mobile (responsive) view no matter what.
    4. Once the button is clicked, it will remember the user preference for all other pages on the site.

    To do this, I set up my meta tag in my master page as follows:

    <meta runat="server" id="viewporttag" name="viewport" content="width=device-width, initial-scale=1.0" />

    Here is my button at the bottom of the page:

    <asp:Button ID="Desktop_button" runat="server" Text="Desktop View" OnClick="Desktop_click" />

    Then in my .css file, the button is hidden by default, unless its CSS class is set to ".clicked", and is set to always show at small screen sizes:

    #Desktop_button
    {
    	margin: 0 0 0 5%;
    	display: none;
    }
    
    	#Desktop_button.clicked
    	{
    		display: inline;
    	}
    }
    
    @media only screen and (max-width: 850px)
    {
    	#Desktop_button
    	{
    		display: inline;
    	}
    }

    Now for the fun part, in my masterpage codebehind:

    protected void Desktop_click(object sender, EventArgs e)
    {
    	//if it's never been set or has been set to Mobile:
    	if (Session["view"] == null || Session["view"].ToString() == "Mobile")
    	{
    		viewporttag.Visible = false; //remove meta viewport tag
    		Session["View"] = "Desktop"; //we are now in desktop view
    		Desktop_button.Text = "Mobile View";
    	}
    	else
    	{
    		viewporttag.Visible = true; //enable meta viewport tag for mobile views
    		Session["View"] = "Mobile";
    		Desktop_button.Text = "Desktop View";
    	}
            addClass(Desktop_button, "clicked");
    }
    protected void Page_Load(object sender, EventArgs e) { //set up defaults based on current view setting. This is called before button click event! if (Session["View"] != null) //if the button has ever been clicked, View won't be null addClass(Desktop_button, "clicked"); //always show the button if it has been clicked, ever if (Session["View"] != null && Session["View"].ToString() == "Desktop") //was it explicitly set? viewporttag.Visible = false; //remove meta viewport tag //now set default text on buttons for freshly loaded pages if (Session["View"] == null || Session["View"].ToString() == "Mobile") Desktop_button.Text = "Desktop View"; else Desktop_button.Text = "Mobile View"; }

    private void addClass(WebControl mycontrol, string cssclass)
    {
        if (mycontrol.CssClass.Split(' ').Contains(cssclass))
        return;
        if (!string.IsNullOrEmpty(mycontrol.CssClass))
         mycontrol.CssClass += " "; //add separator
        mycontrol.CssClass += cssclass;
    }

    This scheme seems to be working great so far. I'm still testing this but I'd love to hear some feedback or if anyone finds this useful.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Thursday, March 7, 2013 9:07 AM
  • User1943143334 posted

    Hi,

    Glad it's working for you :)

    Thursday, March 7, 2013 10:01 AM
  • User1692409842 posted

    I've refactored this into a user control with some improvements. Just having some issues on cached pages I'm hashing my way through. Will be glad to share this with anyone, once I can get it stable and working with my page cache settings, ugh.

    Friday, March 8, 2013 12:37 AM