Friday, April 30, 2010

Dynamically Generate Meta Tags for ASP.NET Pages

create an XML file that contains the data for the meta tags. Right click the website and choose Add > New Item > XML File. Name the file TagData.xml and add the following XML:

<tags pageName="/WebForm1.aspx">
<tag name="keyword" value="This is a keyword"></tag>
<tag name="description" value="This is a description"></tag>
<tag name="author" value="malcolm sheridan"></tag>
</tags>
<tags pageName="/ChildFolder/WebForm1.aspx">
<tag name="keyword" value="This is a keyword for the child pages"></tag>
<tag name="description" value="This is a description for the child pages"></tag>
<tag name="author" value="malcolm sheridan for this page too"></tag>
</tags>


In the XML above I have created a parent node called metaTags. Inside I have created a tags node which contains a pageName attribute. That value is how we will match the current requested page to the XML data. Each tags node contains a tag node that corresponds to the meta data we want sent to the browser. In this example I want to set meta tags for the all the pages to have keyword, description and author meta tags, but the values rendered to the browser will differ depending on what page the user is on. In a real world scenario this information would be stored inside a database, but I decided to keep this data inside an XML file to keep it simple and focus on how to do this.

Having outlined what meta tags we want sent to the browser, we now have to write the code that will read the XML file and dynamically add the meta tags at runtime. Seeing as though we’re using Master Pages this is the ideal spot to add it. Add the following code to read the XML file:

C#

XDocument doc = XDocument.Load(Server.MapPath("~/TagData.xml"));
var metaTags = doc.Descendants("tag")
.Where(o => o.Parent.Attribute("pageName").Value == Request.Url.AbsolutePath)
.Select(o => new
{
Value = o.Attribute("value").Value,
Name = o.Attribute("name").Value
});

VB.NET

Dim doc As XDocument = XDocument.Load(Server.MapPath("~/TagData.xml"))
Dim metaTags = doc.Descendants("tag").Where(Function(o) o.Parent.Attribute("pageName").Value = Request.Url.AbsolutePath).Select(Function(o) New With {Key .Value = o.Attribute("value").Value, Key .Name = o.Attribute("name").Value})

For flexibility and ease of use I have decided to use the power of LINQ to XML to read the XML data. To start with the XML document is load into an XDocument object. From there I have created a LINQ query to return all the tag nodes where the parent node has an attribute called pageName and the value is equal to the current page. Then the object returned is an anonymous type that has a Value and Name property. The values of those properties are the value and name attribute values.

Now that we have the data in memory, the next step is to create the meta tag and add it to the page dynamically. To do this you use the HtmlMeta class. This allows you programmatic access to the HTML meta tags. Add the following code below to your project:

C#

foreach (var item in metaTags)
{
HtmlMeta meta = new HtmlMeta();
meta.Name = item.Name;
meta.Content = item.Value;
Page.Header.Controls.Add(meta);
}

VB.NET

For Each item In metaTags
Dim meta As New HtmlMeta()
meta.Name = item.Name
meta.Content = item.Value
Page.Header.Controls.Add(meta)
Next item

The foreach loop enumerates through each item returned from the LINQ query. It assigns the Name and Content value to the HtmlMeta object. Finally the object is added to the page by calling Page.Header.Controls.Add(meta). Run the project and once the default page has loaded

Change Image Opacity on MouseOver using jQuery

This short article demonstrates how to change the opacity of an image when the user hovers the mouse over an image. This article is a sample chapter from my EBook called 51 Tips, Tricks and Recipes with jQuery and ASP.NET Controls. The chapter has been modified a little to publish it as an article.

Note that for demonstration purposes, I have included jQuery and CSS code in the same page. Ideally, these resources should be created in separate folders for maintainability.

Let us quickly jump to the solution and see how we can change the opacity of an image.

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title>Change Image Opacity on Hovertitle>
<style type="text/css">
.imgOpa
{
height:250px;
width:250px;
opacity:0.3;
filter:alpha(opacity=30);
}
<style>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
<script>
<script type="text/javascript">
$(function() {
$('.imgOpa').each(function() {
$(this).hover(
function() {
$(this).stop().animate({ opacity: 1.0 }, 800);
},
function() {
$(this).stop().animate({ opacity: 0.3 }, 800);
})
});
});
<script>
<head>
<body>
<form id="form1" runat="server">
<div>
<h2>Hover over an Image to change its Transparency level<h2>
<br />
<asp:Image ID="Image1" runat="server"
ImageUrl="../images/1.jpg" class="imgOpa" />
<asp:Image ID="Image2" runat="server"
ImageUrl="../images/2.jpg" class="imgOpa" />
<asp:Image ID="Image3" runat="server"
ImageUrl="../images/3.jpg" class="imgOpa" />
<asp:Image ID="Image4" runat="server"
ImageUrl="../images/4.jpg" class="imgOpa" />
<div>
<form>
<body>
<html>

In this example, observe that the images have a class attribute ‘imgOpa’. The definition of the CSS class is as shown here:
.imgOpa
{
height:250px;
width:250px;
opacity:0.3;
filter:alpha(opacity=30);
}
When the images are loaded, they are in a semitransparent state. In Firefox, Chrome and Safari, we use the opacity:n property for transparency. The opacity value can be from 0.0 - 1.0, where a lower value makes the element more transparent.
In IE 7 and later, we use filter:alpha(opacity=n) property for transparency, where the opacity value can be from 0-100.
When the user hovers the mouse over a semitransparent image, we use the jQuery hover() method to animate the opacity property from 0.3 to 1.0, thereby creating a cool effect on the images. The code to achieve this effect is shown below:
$(this).hover(
function() {
$(this).stop().animate({ opacity: 1.0 }, 800);
},
function() {
$(this).stop().animate({ opacity: 0.3 }, 800);
})
});

Displaying Time Spent on Page

In order to display the time spent on the page, we will have to create an UpdatePanel that updates every second. In order for this to not affect the rest of the page, we will put other functionality in another Update Panel. To programmatically update the time, we will use an AJAX Timer control. But first, let us begin by adding a ScriptManager control to our ASPX page:


<asp:scriptmanager id="SM1" runat="server"></asp:scriptmanager>
The next thing to do is add our UpdatePanel that will display the time spent on the page:

<asp:UpdatePanel ID="UP_Timer" runat="server" RenderMode="Inline" UpdateMode="Always">
<Triggers>
</Triggers>
<ContentTemplate>
</ContentTemplate>
</asp:UpdatePanel>
Notice that we have included the triggers tag, as well as the content template. The trigger we are going to use will be an AJAX Timer control. Let's add that in now, as well as set the Trigger attributes:

<asp:UpdatePanel ID="UP_Timer" runat="server" RenderMode="Inline" UpdateMode="Always">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />
</Triggers>
<ContentTemplate>
<asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick" />

<asp:Literal ID="lit_Timer" runat="server" /><br />
<asp:HiddenField ID="hid_Ticker" runat="server" Value="0" />
</ContentTemplate>
</asp:UpdatePanel>
We add an AsyncPostBackTrigger and set the control ID and Event Name to match our Timer control. We also set the Interval of the Timer's tick event to 1000 milliseconds. We have added a Literal control to display the time spent on the page, and then a HiddenField to store that value. We add code to the code-behind that will run every second, on the Tick event of the Timer control (we can do this by simply adding the code manually, or double-clicking the timer control in Design view):

protected void Page_Load(object sender, EventArgs e)
{
if (! IsPostBack)
{
hid_Ticker.Value = new TimeSpan(0, 0, 0).ToString();
}
}

protected void Timer1_Tick(object sender, EventArgs e)
{
hid_Ticker.Value = TimeSpan.Parse(hid_Ticker.Value).Add(new TimeSpan(0, 0, 1)).ToString();
lit_Timer.Text = "Time spent on this page: " + hid_Ticker.Value.ToString();
}

First, on page load we are assigning a new value to the hidden field, of type TimeSpan. This TimeSpan is 0 hours, 0 minutes, and 0 seconds. Then on each Tick event, we add one second to that value, then display it in the Literal control. If we now run this page, we will see the page programmatically and seamlessly count up the time spent on the page.
To demonstrate that this will not interfere with other functions on the page, let's go ahead and add another Update Panel:
<form id="form1" runat="server">
<asp:ScriptManager ID="SM1" runat="server" />

<asp:UpdatePanel ID="UP_Timer" runat="server" RenderMode="Inline" UpdateMode="Always">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Timer1" EventName="Tick" />

</Triggers>
<ContentTemplate>
<asp:Timer ID="Timer1" runat="server" Interval="1000" ontick="Timer1_Tick" />
<asp:Literal ID="lit_Timer" runat="server" /><br />
<asp:HiddenField ID="hid_Ticker" runat="server" Value="0" />
</ContentTemplate>

</asp:UpdatePanel>

<br />

<asp:UpdatePanel ID="UP_Name" runat="server" RenderMode="Inline" UpdateMode="Conditional">
<ContentTemplate>
Add your name: <asp:TextBox ID="fld_Name" runat="server" /><br />

<br />
<asp:Button ID="btn_Submit" runat="server" Text="Submit" onclick="btn_Submit_Click" /><br />
<asp:Literal ID="lit_Name" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
</form>


using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (! IsPostBack)
{
hid_Ticker.Value = new TimeSpan(0, 0, 0).ToString();
}
}

protected void Timer1_Tick(object sender, EventArgs e)
{
hid_Ticker.Value = TimeSpan.Parse(hid_Ticker.Value).Add(new TimeSpan(0, 0, 1)).ToString();
lit_Timer.Text = "Time spent on this page: " + hid_Ticker.Value.ToString();
}

protected void btn_Submit_Click(object sender, EventArgs e)
{
lit_Name.Text = "Thanks. Your name is: " + fld_Name.Text;
}
}