Blog Posts Tagged jQuery
CSS to jQuery ASP.Net HTTPHandler
In this post I'm going to explain why and how to automatically turn css into jQuery code that is executed on page load. The solution is written for ASP.Net but in theory it could be ported to any other framework.
It's nice to add various visual enhancements and tweaks to websites I develop and I do that using the jQuery Javascript library. These enhancments, in my mind, are not necessary but nice to haves. However, it's important to ensure that web pages are still accessible and render well when Javascript is disabled. So, according to the principles of Progressive Enhancement, I layer the effects on top of the "vanilla" page using jQuery. A simple example is my About page. Try turning Javascript on and off then reloading. Without Javascript each section is displayed inline. With Javascript turned on some jQuery voodoo shows and hides the sections with some nice fade transition effects .
Where am I going with this? Well I find myself writing a lot of jQuery in order to apply some CSS to various elements in order to switch them from vanilla mode into singing and dancing mode. Which means I need to write script to run as the page loads to override the vanilla CSS rules.
And?
I thought it would be nice to author these styles in a way I'm familiar with - style sheets. So I wrote a simple ASP.Net HTTP Handler to parse certain CSS files (I chose the .jqcss file extension) and spit out jQuery instead.
For example:
.test {
background-color: red;
padding: 10px;
background-position: top left;
margin: 5px;
}
h2:first {
color: red;
}
h2[id="3"] {
color: blue;
}
#blue {
background-color: blue;
color: #fff;
padding: 5px;
}
Becomes:
$(document).ready(function(){
$('.test').css({backgroundColor:'red',padding:'10px',backgroundPosition:'top left',margin:'5px'});
$('h2:first').css({color:'red'});
$('h2[id="3"]').css({color:'blue'});
$('#blue').css({backgroundColor:'blue',color:'#fff',padding:'5px'});
});
To use the HTTP Handler you need to add a reference to your handler in your web.config httpHandlers section and tell it to handle .jqcss files, as follows:
<httpHandlers>
<add verb="*" path="*.jqcss" type="IQODE.WebLib.Handlers.JQCSSHandler" />
</httpHandlers>
Then create your .jqcss file containing the style rules you want to be applied on load. Finally you need to add a link to your .jqcss file from your page:
<script type="text/javascript" src="test.jqcss"></script> I'm not absolutely convinced there's a huge need for this technique. It simplified the develoment of my site, iqode.com, and keeps my pages tidier - I'm not a massive fan of having a load of inline Javascript cluttering up my pages. More importantly, by placing this script in an external file I can also manage how the file gets cached by my application and the browser - in ASP.Net I can set a cache dependancy on the .jqcss file itself and only refresh the cache if the file changes. This means the handler only needs to parse the file occasionally.
Here's the entire source code for the HTTPHandler - I removed the cache control code etc. for brevity. One point of interest is the use of an arbitrary Web Control (a button in this case) - so I don't need to parse all the style rules for each selector I load the entire rules string into the Web Controls CssStyleCollection object. This object parses it for me and I simply iterate of the key collection pulling out each rule/value pair to build the jQuery statement.
using System;
using System.Collections;
using System.Web;
using System.Text.RegularExpressions;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace IQODE.WebLib.Handlers
{
public class JQCSSHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Get the physical path of the file being processed
string File = context.Request.PhysicalPath;
string css = string.Empty;
StringBuilder sb = new StringBuilder();
Button btn = new Button();
// Open the file, read css
using (System.IO.StreamReader reader = new System.IO.StreamReader(File))
{
css = reader.ReadToEnd();
}
css = RemoveWhitespace(css);
string[] cssRules = css.Split('}');
sb.AppendLine("$(document).ready(function(){");
foreach (string cssRule in cssRules)
{
if (!string.IsNullOrEmpty(cssRule))
{
string selector = cssRule.Split('{').GetValue(0).ToString().Trim();
string rules = cssRule.Split('{').GetValue(1).ToString();
CssStyleCollection sc = btn.Style;
sc.Value = rules;
rules = "";
foreach (string key in btn.Style.Keys)
{
rules += ConvertToCamel(key) + ":'" + btn.Style[key].Replace("'", "\"") + "',";
}
sb.AppendLine(" $('" + selector + "').css({" + rules.TrimEnd(',') + "});");
}
}
sb.AppendLine("});");
context.Response.ContentType = "text/css";
context.Response.ContentType = "text/javascript";
context.Response.Write(sb.ToString());
}
protected string ConvertToCamel(string s)
{
try
{
if (s.IndexOf('-') < 0)
{
return s;
}
string[] words = s.Split('-');
string word1 = words[0];
string word2 = words[1];
string lttr = word2.Substring(0, 1).ToUpper();
return word1 + lttr + word2.Remove(0, 1);
}
catch (Exception)
{
throw new Exception("Error Parsing jQcss file - malformed css attribute found in stylesheet: " + s);
}
}
public bool IsReusable
{
get
{
return false;
}
}
public static string RemoveWhitespace(string str)
{
try
{
if (string.IsNullOrEmpty(str))
{
return str;
}
return new Regex(@"[\t\r\n]").Replace(str, string.Empty);
}
catch (Exception)
{
return str;
}
}
}
}
And that's it... I hope someone finds this useful. You could simply put all our jQuery css stuff in a separate script file but I found it easier and quicker to do it this way.
Drop me a line if you have any questions, spot a bug or have suggestions for improvement.
Comments: 2 Comments