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 !

3 comments:

Anonymous 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.

Anonymous said...

To solve this problem, I just included a content place holder in my head tag of my master template. In my child pages, I can include extra scripts by putting them in the that content place holder.

That seems like a much simpler solution. Is there any reason not to do it that way?

Anonymous said...

Thank you Anonymous of Jan 19, 2011. you just gave me an idea that saved a lot of time! Content placeholder in head is the way to go!