Monday, January 28, 2008

Adding CSS or JavaScript to your mastertemplate based on documenttype

The way I normally work when making websites is to use several CSS-files and JavaScript-files. So most of the time I have a main.css and general.js file that is included in every page. But when I have pages that needed separate styling or clientside functionality I add for example a news.css and news.js. So I the visitor only has to download these file when needed.

In Umbraco there is no build-in function to append to the <head>-section of your mastertemplate from your childtemplate. To solve this I see several solutions :

  1. Build multiple mastertemplate
  2. Use a macro

Because solution 1 needs a lot of maintenance I decided to build a macro.

How does it work :

You insert this macro in to your mastertemplate. In the macro you decide based on the nodeTypeAlias attribute of the currentPage which CSS-file or JavaScript-file to add.

Here is the XSLT you need

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
  &amp;lt;!ENTITY nbsp "&#x00A0;">
]>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxml="urn:schemas-microsoft-com:xslt"
    xmlns:umbraco.library="urn:umbraco.library"
    exclude-result-prefixes="msxml umbraco.library">


  <xsl:output method="xml" omit-xml-declaration="yes"/>

  <xsl:param name="currentPage"/>

  <xsl:template match="/">

    <xsl:choose>
      <xsl:when test="$currentPage/@nodeTypeAlias = 'News'">
        <xsl:call-template name="css">
          <xsl:with-param name="file">/css/news.css</xsl:with-param>
        </xsl:call-template>
      </xsl:when>
      <xsl:when test="$currentPage/@nodeTypeAlias = 'TextPage'">
        <xsl:call-template name="script">
          <xsl:with-param name="file">/scripts/textpage.js</xsl:with-param>
        </xsl:call-template>
      </xsl:when>
    </xsl:choose>

  </xsl:template>

  <xsl:template name="css">
    <xsl:param name="file" />
    <xsl:element name="link">
      <xsl:attribute name="rel">stylesheet</xsl:attribute>
      <xsl:attribute name="type">text/css</xsl:attribute>
      <xsl:attribute name="href">
        <xsl:value-of select="$file"/>
      </xsl:attribute>
    </xsl:element>
  </xsl:template>

  <xsl:template name="script">
    <xsl:param name="file" />
    <!-- to prevent that a self closing script tag is generated we use the xsl:text-->
    <xsl:text disable-output-escaping="yes"><![CDATA[<script language="javascript" type="text/javascript" src="]]></xsl:text>
    <xsl:value-of select="$file"/>
    <xsl:text disable-output-escaping="yes"><![CDATA["></script>]]></xsl:text>
  </xsl:template> 
</xsl:stylesheet>

If you think there is a way to improve this let me know !

1 comments:

Astuanax said...

Unless you have a lot of CSS files ( over 10) that change very often based on document types (every Doc Type) and they do not have much in comen I can understand this solution.
But if the above numbers don't match, I would go for an Id and CSS selectors based solution.

I always add the name of the doc type as an id to the body.

In the template <body id="<umbraco_field name="DocTypealias">">

this will get you

<body id="textpage">

Then in the style sheet you can modify every element based on the cascade:

/* Any paragraph */
p{ font-size: 1em; color:red}
#textPage p { color: appleblauwzeegroen}

The overhad of the stylesheet is minimal, and if you properly cache the stylesheet there is virtually no overhead.

By the way, welcome to the umbraco community.