Friday, October 14, 2011

jQuery :contains with the exact same value

The task to do:
During my work I had to create a page scrolling effect with a dropdown menu and a (verticaly) very long content. So when someone clicks on a menu item the page has to scroll to the exact position where the content begins.

The idea of the solution:
As the menu-items' text and the title of the content parts are exactly the same, the idea is to find the title tag according to it's content. Then get the Y coordinate of it and scroll the body to this position.

The solution in jQuery:
My first (key) idea was to use jQuery's built-in ":contains" method:
jQuery("h2:contains('Some Value')")

But in this case this won't work perfectly, because there were menu items like "Product" and "Product 2.0". So I needed a ":contains" solution but with the exact values. This is what I came up with:
jQuery("#menu a")
  // disable links. We need only Javascript behavior
  .attr("href", "javascript:void(null)")
  // what shall happen if the link is clicked
  .click(function () {
   // get the text of the menu item
   var text = jQuery(this).text();
   // get the DOM object of the target title (The h2 element wich contains the exact same text of the menu item)
   var target = jQuery('h2').filter(function() {
        return jQuery(this).text() === text;
   });
   // get the position of the target element
   var pos = target.offset();
   // if the position is found...
   if (typeof pos.top != 'undefined') {
    // scroll the page there
    jQuery.scrollTo((pos.top-10) + "px", 700);
   }

  });



Note: I used Ariel Flesler's ScrollTo jQuery plugin. (Thanks Ariel by the way..)

Thursday, October 13, 2011

XHTML Strict and target _blank


This topic is quite a challange because on one side I want my websites "strict" valid. On the other hand, sometimes it's important (at least clients say so) to open links in a new window, so I did a little research (and there are a lot of informations out there to this problem)

What are the solutions for being valid and in the same time opening links in a new window?

1. I tried it with CSS: (I know, I know... CSS is for styling only, not for behavior.. I'm searching solutions right now)
Theoretically it can be done with CSS3. According to this page the code should look like this:

a {
    target-name: new;
    target-new: tab;
}

But none of the browsers responded to it so this solution is precluded.



2. Javascript:
Here we have quite interesting solutions.

Manually open a new window with "window.open(URL)":

Inline mode:
<a href="http://thinkrement.blogger.com" onclick="window.open(this.href); return false;">Read Thinkrement!</a>

This works, however it's not so elegant and it's better to keep the HTML and Javascript seperate.

So, let's improve it a bit:

HTML:
<a href="http://thinkrement.blogger.com" rel="popup">Read Thinkrement!</a>

Javascript:
window.onload = function() {
    var links = document.getElementsByTagName('a');
    for (var i=0;i < links.length;i++) {
        if (links[i].rel == 'popup') {
            links[i].onclick = function() {
                window.open(this.href);
                return false;
            };
        }
    }
};


or with jQuery:
$('a[rel="popup"]').click(function() {
    window.open(jQuery(this).attr("href"));
    return false;
});



The constrained method:

With Javascript it's similar to the previous "window.open" method with the difference, that instead of "window.open" we use:
window.onload = function() {
    var links = document.getElementsByTagName('a');
    for (var i=0;i < links.length;i++) {
        if (links[i].rel == 'popup') {
            links[i].target="_blank";
        }
    }
};

This is quite simple, however this solution is not supported by IE and Chrome.

With jQuery it a bit easier (only 1 line):
$('a[rel="popup"]').attr("target", "_blank");




3. HTML:

- Use a Transitional Doctype
- Have some "target" errors on your site
- or put it out of your mind that you want to use the target attribute. It is said that for usability reasons it's better to let users decide whether they want a new window (tab) or not.

Wednesday, October 12, 2011

Javascript Canvas trumps all others

Recently I have made a little test. I would have like to make a little active-background effect on my project and I have faced a problem with the hardware resources. What you have to know about this project is, that it will be an information panel on my college where I graduated. So, the hardware, the browser and the resolution (and pretty much everything else) is fixed.

This is the DEMO (and a simplified result):
http://jsfiddle.net/FilipFlora/cLByF/10/embedded/result/

The task was:
Create an active-background animation when the tablet is idle. So when you walk by, it'll catch your attention..

The problem:
The hardware is rather energy-efficient and has only a basic graphics-card integrated. The images to move are huge (circa 2000x1600) and they have a 5% opacity on them. (Just as I wrote these lines, I thought that another solution could be to set the opacity value with css and use gifs instead?? But I don't think that this would be better than the solution below)

The attempts to solve the problem: (not in this order, but I was diligent to find the best looking solution :) )
1. attempt: Background-image (for this I used the background-position jQuery plugin)
I tried that..yes. I created 4 layers, I have set the background-image with CSS, and created the animation with jQuery.

HTML:
<div id="index_bg_1">&nbsp;</div>
<div id="index_bg_2">&nbsp;</div>
<div id="index_bg_3">&nbsp;</div>
<div id="index_bg_4">&nbsp;</div>

CSS:
#index_bg_1,
#index_bg_2,
#index_bg_3,
#index_bg_4 {
 background-image: url("../images/index-bg-1.png");
 background-repeat: no-repeat;
 background-position: 0px 0px;
 width: 1680px;
 height: 1050px;
 position: absolute;
 left: 0px;
 top: 0px;
}

#index_bg_2 {
 background-image: url("../images/index-bg-2.png");
 background-position: 0px 0px;
}

#index_bg_3 {
 background-image: url("../images/index-bg-3.png");
 background-position: 0px 0px;
}

#index_bg_4 {
 background-image: url("../images/index-bg-4.png");
 background-position: 0px 0px;
}

Javascript:
jQuery(document).ready(function() {

 // initiate all background-image animation
 for( var i = 1; i<5; i++) {
  animateBackgroundImage("#index_bg_"+i);
 }
 
});

function animateBackgroundImage(selector) {
 
 // new target x coodinate
 var pos_x = Math.rand(-1200, 1600);
 // current x coordinate 
 var bg_x = ((jQuery(selector).css("background-position")).replace("px 0px", ""))*1;
 // animation time
 var anim_time = Math.abs(pos_x-bg_x)/24*1000;
 
 // to do this, you need the backgroundPosition plugin for jQuery
 jQuery(selector).animate({
  backgroundPosition: "("+pos_x+"px 0px)" // to keep it simple, let's animate only the x value
 }, anim_time, "linear" );
 
 // after the animation is done, create another
 setTimeout("animateBackgroundImage('"+selector+"')", anim_time);
}
// this is just a helper function to create php like random number generator
Math.rand = function (min, max) {
 return(Math.round(Math.random() * (max-min)) + min);
}


As it turned out, this was too much for the little machine..


2. attempt: with simple images
I don't want to make this post too long so I summarize it briefly. It doesn't went well. It was a little bit faster, but I needed much more.

3. attempt: Javascript Canvas
This has rocked my world again. After I have read Zsolti's article about canvas animations (thank you my dear friend :) ) I was very excited to test it on the real thing (as I was developing on my laptop). Needless to say.. it went perfectly.

Here is the code:

HTML:
<canvas width="1680" height="1050" id="index_canvas"></canvas>

Javascript:
// animation object
var indexAnim = {
 images: [], // here are the image objects
 positions: [], // the actual positions of the images
 destPositions: [] // the target coordinates of the image
}

jQuery(document).ready(function() {
 
 // init
 for( var i = 1; i<=4; i++) {
  
  // create a new image to place on canvas
  var im = new Image();
  im.src = 'images/index-bg-'+i+'.png';  
  indexAnim.images[i-1] = im;
  // place it randomly on canvas
  indexAnim.positions[i-1] = Array(Math.rand(-500, 500), Math.rand(-500, 500)); 
  // set it's destination positions
  indexAnim.destPositions[i-1] = Array(Math.rand(-500, 500), Math.rand( -500, 500));
 
 }
 
 // just for a better performance
 var length = indexAnim.images.length;
 var canvas = document.getElementById('index_canvas').getContext('2d');
 
 // let the animation begin (... WHA-HA-HAAA)
 setInterval(function () {
  
  // clear the image
  canvas.clearRect(0,0,1680,1050);  
  
  // let's redraw all the images
  for( var i = 0; i<length; i++ ) {
   
   // if a new target position is needed (either x or y)
   if( Math.abs(indexAnim.positions[i][0] - indexAnim.destPositions[i][0]) < i+1 || Math.abs(indexAnim.positions[i][1] - indexAnim.destPositions[i][1]) < i+1 ) {
    indexAnim.destPositions[i] = Array(Math.rand(0, 1050), Math.rand( -500, 500));
   }
   
   // set the new position of the image. It's only 1 pixel closer to the destination coordinate (Just like in life...)
   indexAnim.positions[i][0] = indexAnim.positions[i][0] + (indexAnim.positions[i][0] < indexAnim.destPositions[i][0] ? 1: -1);
   indexAnim.positions[i][1] = indexAnim.positions[i][1] + (indexAnim.positions[i][1] < indexAnim.destPositions[i][1] ? 1: -1);
   
   // draw image on canvas
   canvas.drawImage(indexAnim.images[i], indexAnim.positions[i][0],indexAnim.positions[i][1]);  
  
  }
 
  
 }, 41); // 41ms = 1000/24. 24 Frames are needed at least to create a smooth flow..
 
});

Math.rand = function (min, max) {
 return(Math.round(Math.random() * (max-min)) + min);
}


Maybe the animation needs some fine-tuning, but It's quite good :)

4. possible attempt: Flash
But since I don't do Flash programming, this solution depends on my brother :)


I hope you like my little solution here, and if you have a better idea share it with us.

Monday, October 10, 2011

jQuery caching

It's really simple to select an element in jQuery:

$('#name');

But if we use this selection more than once in our code, we should cache it:

var name = $('#name');
name.val("Johnson");
name.addClass("inputField");
name.parent().append('<p>Some text here</p>');

Every time we select an element with jQuery, the framework goes through the entire DOM until it founds all the elements that match the selector. In the above example, I cached the jQuery object to a variable called name. Later on my code, I can use this variable to modify my element without searching for it again. So I've just quartered the time spent on selection which means I've given a boost to performance.

If I cache multiple elements (for example: form inputs) I usually use an object literal to group them together. So later on my code, I won't get confused which variable relates to which form.

/* Registration form */
var regForm = {
 'name'  : $('#name'),
 'email'  : $('#email'),
 'address' : $('#address'),
 'phone'  : $('#phone') 
};

var contactForm = {
 'name'  : $('#name'),
 'email'  : $('#email'),
 'subject' : $('#subject'),
 'message' : $('#message') 
};

// ... some other code here

if(regForm.name.val() == "") {
 // do something
}

// ... some other code here

if(contactForm.name.val() == "") {
 // do something
}

Sunday, October 9, 2011

Javascript Array prototype and for-in

If you are developing a 3rd party application, a jQuery plugin or something similar, this will be specially useful for you.

I'm sure you've heard about it but you can extend the Javascript's core objects with the help of the prototype property. So, in this case let's see what happens if we combine Array.prototype with a for-in loop.

// let's create a simple array
var testArr = new Array(1, 2, 3);

// print it out to the console
for( var i in testArr ) {
 console.log(i+": "+testArr[i]);
}
// output: 
// 1
// 2
// 3

// extend the array object
Array.prototype.myMethod = function (arr) {
 // Do something here..
}

// loop the array again
for( var i in testArr ) {
 console.log(i+": "+testArr[i]);
}
// output: 
// 1
// 2
// 3 
// myMethod: function (arr) { }


As you can see, the second time in the loop the custom methos is also listed az an array item. This can be source of unknown bugs and I have spent a lot of time to figure this little trick out.

So, what are the solutions?
Use the Array object's "hasOwnProperty" property:
for( var i in arr) {
      if( !arr.hasOwnProperty( i ) ) continue;
      // stuff to do..
}

Or if you have jQuery loaded, use the .each method.
jQuery.each(testArr, function (key, value) {
 console.log(key+": "+value);
});


This possible bug source is kind of sneaky, yet the solution is simple :)

HowTo: Canvas colorful bouncing circle animation with JavaScript

Introduction


I've really really waited for the moment when I can start developing canvas animations. In this post I'm going to present you what I've coded and tell you a few words about its concept.

But, check out the Demo first.

So, as you've seen, the result is a canvas with bouncing and colorful circles. They are random positioned with random colors, random directions and random sizes. But, what is a canvas? Canvas is a part of Html5 specification. You can use it to draw graphics and animations with the help of JavaScript.

<canvas width="400" height="300"></canvas>

I won't go into basics of canvas element. If you are interested about it, please check out Mozilla Developer Network's Canvas Tutorial.

Animation basics

Frames. All we need is them. A frame is a 'screenshot' of the current state of a scene. A scene contains the objects and pretty much everything we want to draw to the audience. We create an animation from frames by drawing at least 24 to the screen in every second. This is the minimum amount to deceive the eye to see motion on the screen. Of course, the more frames we draw in one second, the smoother the animation gets. From frame to frame we must update and redraw all of the objects. Everytime we create an animation the following things happen:
  1. Initialization
  2. Updating scene
  3. Drawing scene
  4. While it must be drawn, jump back to the 2. point else 5.
  5. End of animation

The 2., 3. and 4. point together forms the main loop. This is where all of the scene objects' state, position, color etc. gets updated and gets drawn. If you wish to put user input to the animation (etc, mouse movement) there would be a new item in the list for input handling.

Skeleton of my animation object

var animation = function() {
 /* List of objects to draw (the particles) */
 var list = [];

 /* Frame per seconds */
 var fps = 24;

 /* Object constructor */
 var particle = function() {};
 
 /* Initialization */
 this.init = function(v) {};

 /* Updating objects state, position, etc.. */
 var update = function() {};

 /* Drawing the objects */
 var draw = function() {};

 /* Calling update() and draw() */
 var loop = function() {};

 /* Setting main loop */
 this.play = function() {};
}

The above source snippet is the skeleton of my animation object.

Initialization

Bouncing circles on the screen represented as a particle object. The animation itself has a variable called list. Every item of it will be a new particle object. Calling the init() function with a number parameter will add as much particles to the animation as much we want.

Particles

Particles object looks like this:
/* Particle object */
  var particle = function() {
    /* Coordinates */
    this.x = 0;
    this.y = 0;

    /* The radius of circles */
    this.radius = 5;

    this.speed_x = 1;
    this.speed_y = 1;

    /* Direction */
    this.dx = 0;
    this.dy = 0;

    this.color = {
      fill : '#000',
      stroke : '#000'
    }

    /* Boundaries (canvas width and height) */
    this.bounds = {
      x0 : 0,
      x1 : 600,
      y0 : 0,
      y1 : 400
    }

    /* Private function for random color but I think you've already guessed that. */
    var random_color = function(){}

    /* Function to initialise variables */
    this.init = function() {}

    /* Updater function, called at every frame. It updates positions and check boundaries. */
    this.update = function() {}
  }

To represent a circle on the screen I stored information about it such as its x and y coordinate, its x and y speed, its direction, its colors and its boundaries because I don't want it to go away from the screen leaving a blank white field to the user. Init function sets variables to random values. The update function is used to update the circles properties in every loop. It will be called by the loop's update function.

Updating and drawing

I've defined a loop function. It simply calls the update() and draw() once. To create the animation by calling the loop function multiple times in every second I have to use the setInterval() function.

/* To start animation */
this.play = function() {

 /* Animloop */                        
 setInterval(loop, 1000/fps);          
      
};

The first parameter must be a function what will be called, and the second parameter must be a number representing a ms value. Let's say I want it to call 24 times a second, then I must divide 1000 by the fps rate (24 now) to get the desired ms values.


var ANIM = new animation();
ANIM.init(25);
ANIM.play();

Final words

This is a basic animation concept. I hope you found it useful. Play with it online on this link at JsFiddle.net. If you would like to say anything or noticed something, please leave a comment.

jQuery: Checkbox howto

Once I've run into a project with a complicated form where some ajax didn't work. After digging into the code I've sadly seen a mistake that caused the functionality loss.

As you know, checkbox state can be determine at least two ways:
/* checkbox */
var chk1 = $('#chk1');   
               
chk1.attr('checked');
/* or */
chk1.is(':checked');

The previous programmer used the attr() function, however he forgot (or did not know...?) the difference between the two. While attr() function returns the attribute of the checkbox (undefined if not checked, 'checked' if checked), the second is() function returns boolean true or false. He sent the return value of attr() call as a string to a php script, but he expected 'true' or 'false', but he got 'checked' or 'undefined'.

/* checkbox */
var chk1 = $('#chk1');
chk1.attr('checked', true);

// returns 'checked' but it evaluates to boolean true in javascript
if(chk1.attr('checked')) {
    
}

// returns boolean true                
if(chk1.is(':checked')){
    
} 

Another note, to check a checkbox you can use:
chk1.attr('checked',true);

To uncheck a checkbox, you can do:
chk1.attr('checked', false);
/* or */
chk1.removeAttr('checked');

So the conclusion is to be careful out there and always know what a function will return!