Showing posts with label jquery. Show all posts
Showing posts with label jquery. Show all posts

Wednesday, December 22, 2010

Animated scrolling background with parallax using jQuery

I wanted to distinguish the author comments on my blog in an interesting way. Here's what I came up with:


Click the image to see it live

Notice in the live view that there is a parallax effect: some of the clouds appear to float by closer than others. This is accomplished using two images with one on top of the other. The images used are these:
Background:
Transparent overlay:

Implementation

Two css classes are used, one for each image:
.byAuthor
{
color: Black;
background-image: url("http://lh4.ggpht.com/abbrev_path/sky.png");
padding: 5px;
}

.byAuthorOverlay
{
background-image: url("http://lh4.ggpht.com/abbrev_path/sky2.png");
}

Eventually our comments will take the form of two nested divs, each given one of the classes defined above with the overlay on top. For example:
<div class="byAuthor">
<div class="byAuthorOverlay">
Here is the text of the comment
</div>
</div>

Next comes the javascript. The setInterval() method is used to call a function which changes the Y offset of the background image. This is done twice: once for each image.
function scrollBackground(options) {
options.step = options.step || 1; //step property defaults to 1
options.top = options.top === undefined || Math.abs(options.top) >= options.bgImgHeight ? 0 : options.top - options.step;
options.$elements.css("background-position", "0 " + options.top + "px");
}

var mainBg = { speed: 30, bgImgHeight: 400 };
setInterval(function () { scrollBackground(mainBg); }, mainBg.speed);

var overlayBg = { speed: 50, bgImgHeight: 400 };
setInterval(function () { scrollBackground(overlayBg); }, overlayBg.speed);

scrollBackground() takes an object named "options" as an argument. Here is the definition of this object:
options = {
delta: 1, //Y delta - number of pixels by which to change the background-position each time
speed: 30, //time interval between animation steps (milliseconds)(lower is faster)
bgImgHeight: 400px, //height of the image being scrolled
$elements: $(".byAuthor") //a jquery set of elements to apply css changes to
}

setInterval() causes scrollBackground() to be called every X milliseconds. scrollBackground() calculates a new Y value for the background image and applies it. Notice that the "speed" values for each of the images is different. This causes the overlay image to scroll a little faster than the background image providing the parallax effect.

Applying to blogger

Getting this to work on blogger requires understanding of jQuery and the DOM. Because templates differ, you'll need to examine how blogger displays your comments, taking note of the DOM elements surrounding them. In my case, the DOM looks like this (trimmed for brevity):
<dt class="comment-author blog-author">Joel said... </dt>
<dd class="comment-body">
<p>
Comment Text
</p>
</dd>

I want to enclose the comment text within the two nested divs mentioned above. This is simple using jQuery:
    $(".blog-author").next().find("p").wrap('<div class="byAuthorOverlay" />');
$(".byAuthorOverlay").wrap('<div class="byAuthor" />');

That results in the following DOM layout:
<dt class="comment-author blog-author">Joel said... </dt>
<dd class="comment-body">
<div class="byAuthor" >
<div class="byAuthorOverlay" >
<p>
Comment Text
</p>
</div>
</div>
</dd>

And now, with both setInterval() functions executing at the prescribed intervals, the background images for the new divs appear to scroll upward.

Thursday, April 09, 2009

Referencing jQuery on a masterpage without breaking intellisense

If you are like me, you were pretty excited when Microsoft added support for jQuery to intellisense in visual studio. I am not going to go into how this is accomplished. If you would like details you can read Scotts blog here.

The Problem
When referencing jquery through a masterpage, you are vulnerable at runtime to the path of the content page. So if you keep your masterpages in a folder like "/Masters" but the content page you are using is in the root, a jquery path like this won't work:

<script src="../js/jquery-1.3.2.js" type="text/javascript"></script>

Intellisense works fine with this, but at runtime the path is wrong when using the content page in the root. To get the correct path at runtime we can do something like this:

<script src="<%= VirtualPathUtility.ToAbsolute("~/js/jquery-1.3.2.min.js") %>" type="text/javascript"></script>

With that reference we get the correct reference at runtime, but we get no intellisense.

The Solution
Until this problem is fixed by Microsoft, we need to use the best of both worlds. A static reference for the IDE, but a dynamic reference at runtime. To do this we use a condition on the reference that we don't want at runtime, like this:

<script src="<%= VirtualPathUtility.ToAbsolute("~/js/jquery-1.3.2.min.js") %>" type="text/javascript"></script>
<% if (this == null) { %>
    <script src="../js/jquery-1.3.2.js" type="text/javascript"></script>
<% } %>

You can replace the "this == null" with a simple "false" but you will get unreachable code warnings. You just need anything that always evaluates to false. This works great and has very little overhead in your page. I am happy that Microsoft added intellisense support for jQuery, now they just need to finish it so we don't have to use kludges like this.

Thursday, February 12, 2009

Getting a reference to a behavior

For my work I use databound repeating controls extensively. Of course, when using a repeating server control, child control ids are mangled by asp.net to prevent naming conflicts. To facilitate client side interaction I have begun using jQuery to get a reference to DOM elements whose names have been mangled by asp.net.

I wanted to be able to dismiss ModalPopupExtenders on my pages by pressing the escape key and the first obvious problem that manifested was that in order to call the hide() method of the ModalPopupExtender behavior, I had to have a reference to it. But because the ModalPopupExtender is a Component and not a DOM object, I cannot use jQuery to select it.

In my next post I'll demonstrate how I ultimately implemented the functionality I desired, but for now I merely wish to describe a couple of ways to get a reference to a behavior.

I used the Sys.UI.Behavior.getBehaviorsByType() method which works like this:

var mpeArray = Sys.UI.Behavior.getBehaviorsByType($get("btnCausePopup"), 
            AjaxControlToolkit.ModalPopupBehavior);


getBehaviorsByType() returns an array of behaviors. The first parameter it takes is the TargetControlID of the Ajax Control Toolkit control, or in other words, the control that is being extended by the behavior. Here's what my ModalPopupExtender looks like:

 <ajaxToolkit:ModalPopupExtender runat="server" ID="mpePopup" TargetControlID="btnCausePopup"
        PopupControlID="pnlPopup" />


The second parameter is the type of the Sys.UI.Behavior objects to find. Be sure to include the AjaxControlToolkit namespace. In this case the type is "AjaxControlToolkit.ModalPopupBehavior," not "AjaxControlToolkit.ModalPopupExtender." If you are not sure what the type is for the control you are using, one way to find it is to view the source of the page from the browser and search for the value of the TargetControlID. You will find something that looks something like this:

Sys.Application.add_init(function() {
    $create(AjaxControlToolkit.ModalPopupBehavior, {"PopupControlID":"pnlPopup","id":"mpePopup"},
       null, null, $get("btnCausePopup"));


Here, the bold text indicates the Type. Once you have the array of behaviors, iterate through the array to access each individual behavior, setting properties or calling methods at your leisure.

Another useful method in this regard is Sys.UI.Behavior.getBehaviorByName(). Its first parameter is the same as getBehaviorsByType. The second parameter is the name property of the behavior.