Handling Ajax Forms in Colorbox

In my last post I talked about how to add links to other pages with Colorbox. This post is about how to handle Ajax forms with Colorbox. You may have noticed that when you submit a form from within Colorbox, it does a HTTP request rather than an Ajax request. This can be somewhat frustrating – especially if you are wanting to have your form submit via Ajax.

In this post we will cover the following:

  1. Create a small form with required fields.
  2. Create PHP processing to check the required fields.
  3. Open the form with Colorbox.
  4. Submit the form via Ajax and show the results in Colorbox.

Create a small form with required fields

First things first, let’s create our form. The form I am building has 4 fields. Here is the HTML for my form:

01 <form id="pre-process" action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
02   <table>
03     <tr>
04       <td><span>*</span><label for="company-name">Your Company Name:</label></td>
05       <td><input type="text" name="company-name" id="company-name" value="<?php if(isset($_POST['company-name'])){echo $_POST['company-name'];} ?>" /></td>
06     </tr>
07     <tr>
08       <td><span>*</span><label for="name">Your Name:</label></td>
09       <td><input type="text" name="name" id="name" value="<?php if(isset($_POST['name'])){echo $_POST['name'];} ?>"/></td>
10     </tr>
11     <tr>
12       <td><span>*</span><label for="email">Your Email:</label></td>
13       <td><input type="text" name="email" id="email" value="<?php if(isset($_POST['email'])){echo $_POST['email'];} ?>"/></td>
14     </tr>
15     <tr>
16       <td><label for="fax">Your Fax #:</label></td>
17       <td><input type="text" name="fax" id="fax" value="<?php if(isset($_POST['fax'])){echo $_POST['fax'];} ?>"/></td>
18     </tr>
19   </table>
20   <p>
21     <input type="hidden" name="submit" />
22     <input type="submit" name="submit" value="Submit" />
23   </p>
24 </form>

Required fields are denoted by an asterisk. In my form I have three required fields and a fourth field for fax number. Now I’m going to check whether these required fields are filled in with a quick PHP script.

Create PHP processing to check the required fields

This PHP Script is meant to be easily update-able – so if I were to add more fields to my form I could easily check and save them. Insert this code just above your HTML code.

01 <?php
02   if(isset($_POST['submit']))
03   {
04     //define required fields
05     $required_fields = array(
06       'company-name' => 'Your Company Name',
07       'name' => 'Your Name',
08       'email' => 'Your Email'
09     );
10
11     //define other fields
12    $normal_fields = array(
13      'fax' => 'Your Fax #'
14    );
15
16  //get any missing fields
17  $missing_fields = array_diff_key($required_fields, $_POST);
18
19  //only process if required fields exist
20  if(empty($missing_fields))
21  {
22    //merge the required and normal arrays
23    $defined_fields = array_merge($required_fields, $normal_fields);
24
25    foreach($defined_fields as $key => $value)
26    {
27      //add to missing fields if the required value isn't filled - this is checked and displayed later
28      if(array_key_exists($key, $required_fields) && empty($_POST[$key]))
29      {
30        $missing_fields[$key] = $required_fields[$key];
31      }
32      else
33      {
34        $defined_fields[$key] = $_POST[$key];
35      }// if
36    }// foreach
37   //if there are missing fields or errors show them here.
38   if(!empty($missing_fields))
39   {
40     echo '<p>Please fill in the following required fields.</p>';
41     echo '<ul style="color: red;">';
42     foreach($missing_fields as $key => $value)
43     {
44       printf('<li>%s</li>', $value);
45     }// foreach
46     echo '</ul>';
47   }
48   else
49   {
50     echo 'Ajax Form Submission via Colorbox Successful!';
51   }// if
52
53 //display the form if necessary
54 if(!isset($_POST['submit']) || !empty($missing_fields)):
55 ?>
56 <!--HTML FORM FROM ABOVE IN HERE-->
57 <?php endif; ?>

Open the form with Colorbox

Ok, now we’ve got our server side PHP script ready and our HTML form ready. It’s time to open this with Colorbox. I want this form to pop up in a Colorbox when the user clicks a link. Here is the HTML for my link:

1 <a href="myform.php" class="cbox-form">Fill out my Ajaxed Colorbox form!</a>

In order to open the form with Colorbox we need to add the following JavaScript to our page. This activates Colorbox on our form link.

1 jQuery(function(){
2    jQuery('.cbox-form').colorbox({maxWidth: '75%'});
3 });

Great! Now our form is opening inside our Colorbox. If you click our Submit button now you will see that the page does a normal HTTP request and it refreshes. We want it to submit via Ajax – so here is what we do!

Submit the form via Ajax and show the results in Colorbox

Colorbox comes with an assortment of callback functions. These are described in detail on the Colorbox page. We are going to be making use of the onComplete callback.

Locate where you activated your Colorbox in the previous step. We will be modifying this to use the onComplete callback. Replace the previous code with this new code:

01 jQuery('.cbox-form').colorbox({maxWidth: '75%', onComplete: function(){
02   jQuery("#pre-process").submit(function(){
03     jQuery.post(
04       jQuery(this).attr('action'),
05       jQuery(this).serialize(),
06       function(data){
07         jQuery().colorbox({html: data});
08       }
09     );
10    return false;
11  });
12 }});

If you test your form now you will see that it submits via Ajax now. If you fill all of your fields in you will see the “Ajax Form Submission via Colorbox Successful!” message. If you do not fill in some of the required fields then you sill see the Ajax display that you need to fill in some more fields.

Ah! Did you notice? The form submits via Ajax and shows the results – but if the required fields were not filled in, the next submission goes back to the old HTTP request method. Grrr! We will need to perform one more tweak in order to get it to work correctly.

We are going to create another function and use the onComplete callback once again. Replace the old JavaScript with the following final JavaScript:

01 jQuery(function(){
02   jQuery('.cbox-form').colorbox({maxWidth: '75%', onComplete: function(){
03     cbox_submit();
04   }});
05 });
06
07 //submit ajax form continually if needed
08 function cbox_submit()
09 {
10   jQuery("#pre-process").submit(function(){
11     jQuery.post(
12       jQuery(this).attr('action'),
13       jQuery(this).serialize(),
14       function(data){
15         jQuery().colorbox({html: data, onComplete: function(){
16           cbox_submit();
17         }});
18       }
19     );
20     return false;
21   });
22 }// cbox_submit

Alright! Now we have a fully functioning Ajax form that works inside Colorbox.

Advertisements
By Rz Rasel Posted in jQuery

Rich-Text Editing in Mozilla

Introduction

Mozilla 1.3 introduces an implementation of Microsoft® Internet Explorer’s designMode feature. The rich-text editing support in Mozilla 1.3 supports the designMode feature which turns HTML documents into rich-text editors. Starting in Firefox 3, Mozilla also supports Internet Explorer’s contentEditable attribute which allows any element to become editable or non-editable (the latter for when preventing change to fixed elements in an editable environment).

Setting Up Rich-Text Editing

Rich-text editing is initialized by setting the designMode property of a document to “On”, such as the document inside an iframe. Once designMode has been set to “On”, the document becomes a rich-text editing area and the user can type into it as if it were a textarea. The most basic keyboard commands such as copy and paste are available, all others need to be implemented by the website.

Similarly, setting contentEditable to “true” allows you to make individual elements of a document editable.

Executing Commands

When an HTML document has been switched to designMode, the document object exposes the execCommand method which allows one to run commands to manipulate the contents of the editable region. Most commands affect the document’s selection (bold, italics, etc), while others insert new elements (adding a link) or affect an entire line (indenting). When using contentEditable, calling execCommand will affect the currently active editable element.

1
execCommand(String aCommandName, Boolean aShowDefaultUI, String aValueArgument)

Arguments

String aCommandName
the name of the command
Boolean aShowDefaultUI
whether the default user interface should be shown. This is not implemented in Mozilla.
String aValueArgument
some commands (such as insertimage) require an extra value argument (the image’s url). Pass an argument of null if no argument is needed.

Commands

backColor
Changes the document background color. In styleWithCss mode, it affects the background color of the containing block instead. This requires a color value string to be passed in as a value argument. (Internet Explorer uses this to set text background color.)
bold
Toggles bold on/off for the selection or at the insertion point. (Internet Explorer uses the STRONG tag instead of B.)
contentReadOnly
Makes the content document either read-only or editable. This requires a boolean true/false to be passed in as a value argument. (Not supported by Internet Explorer.)
copy
Copies the current selection to the clipboard. Clipboard capability must be enabled in the user.js preference file. See
createLink
Creates an anchor link from the selection, only if there is a selection. This requires the HREF URI string to be passed in as a value argument. The URI must contain at least a single character, which may be a white space. (Internet Explorer will create a link with a null URI value.)
cut
Cuts the current selection and copies it to the clipboard. Clipboard capability must be enabled in the user.js preference file. See
decreaseFontSize
Adds a SMALL tag around the selection or at the insertion point. (Not supported by Internet Explorer.)
delete
Deletes the current selection.
enableInlineTableEditing
Enables or disables the table row and column insertion and deletion controls. (Not supported by Internet Explorer.)
enableObjectResizing
Enables or disables the resize handles on images and other resizable objects. (Not supported by Internet Explorer.)
fontName
Changes the font name for the selection or at the insertion point. This requires a font name string (“Arial” for example) to be passed in as a value argument.
fontSize
Changes the font size for the selection or at the insertion point. This requires an HTML font size (1-7) to be passed in as a value argument.
foreColor
Changes a font color for the selection or at the insertion point. This requires a color value string to be passed in as a value argument.
formatBlock
Adds an HTML block-style tag around the line containing the current selection, replacing the block element containing the line if one exists (in Firefox, BLOCKQUOTE is the exception – it will wrap any containing block element). Requires a tag-name string to be passed in as a value argument. Virtually all block style tags can be used (eg. “H1”, “P”, “DL”, “BLOCKQUOTE”). (Internet Explorer supports only heading tags H1 – H6, ADDRESS, and PRE, which must also include the tag delimiters < >, such as “<H1>”.)
forwardDelete
Deletes the character ahead of the cursor‘s position.  It is the same as hitting the delete key.
heading
Adds a heading tag around a selection or insertion point line. Requires the tag-name string to be passed in as a value argument (i.e. “H1”, “H6”). (Not supported by Internet Explorer.)
hiliteColor
Changes the background color for the selection or at the insertion point. Requires a color value string to be passed in as a value argument. UseCSS must be turned on for this to function. (Not supported by Internet Explorer.)
increaseFontSize
Adds a BIG tag around the selection or at the insertion point. (Not supported by Internet Explorer.)
indent
Indents the line containing the selection or insertion point. In Firefox, if the selection spans multiple lines at different levels of indentation, only the least indented lines in the selection will be indented.
insertBrOnReturn
Controls whether the Enter key inserts a br tag or splits the current block element into two. (Not supported by Internet Explorer.)
insertHorizontalRule
Inserts a horizontal rule at the insertion point (deletes selection).
insertHTML
Inserts an HTML string at the insertion point (deletes selection). Requires a valid HTML string to be passed in as a value argument. (Not supported by Internet Explorer.)
insertImage
Inserts an image at the insertion point (deletes selection). Requires the image SRC URI string to be passed in as a value argument. The URI must contain at least a single character, which may be a white space. (Internet Explorer will create a link with a null URI value.)
insertOrderedList
Creates a numbered ordered list for the selection or at the insertion point.
insertUnorderedList
Creates a bulleted unordered list for the selection or at the insertion point.
insertParagraph
Inserts a paragraph around the selection or the current line. (Internet Explorer inserts a paragraph at the insertion point and deletes the selection.)
insertText
Inserts the given plain text at the insertion point (deletes selection).
italic
Toggles italics on/off for the selection or at the insertion point. (Internet Explorer uses the EM tag instead of I.)
justifyCenter
Centers the selection or insertion point.
justifyFull
Justifies the selection or insertion point.
justifyLeft
Justifies the selection or insertion point to the left.
justifyRight
Right-justifies the selection or the insertion point.
outdent
Outdents the line containing the selection or insertion point.
paste
Pastes the clipboard contents at the insertion point (replaces current selection). Clipboard capability must be enabled in the user.js preference file. See
redo
Redoes the previous undo command.
removeFormat
Removes all formatting from the current selection.
selectAll
Selects all of the content of the editable region.
strikeThrough
Toggles strikethrough on/off for the selection or at the insertion point.
subscript
Toggles subscript on/off for the selection or at the insertion point.
superscript
Toggles superscript on/off for the selection or at the insertion point.
underline
Toggles underline on/off for the selection or at the insertion point.
undo
Undoes the last executed command.
unlink
Removes the anchor tag from a selected anchor link.
useCSS Deprecated
Toggles the use of HTML tags or CSS for the generated markup. Requires a boolean true/false as a value argument. NOTE: This argument is logically backwards (i.e. use false to use CSS, true to use HTML). (Not supported by Internet Explorer.) This has been deprecated; use the styleWithCSS command instead.
styleWithCSS
Replaces the useCSS command; argument works as expected, i.e. true modifies/generates style attributes in markup, false generates formatting elements.

Internet Explorer Differences

One major difference between Mozilla and Internet Explorer that affects designMode is the generated code in the editable document: while Internet Explorer uses HTML tags (em, i, etc), Mozilla 1.3 will generate by default spans with inline style rules. The useCSS command can be used to toggle between CSS and HTML markup creation.

Figure 1 : Generated HTML differences

Mozilla:

1
2
3
<span style="font-weight: bold;">I love geckos.</span>
<span style="font-weight: bold; font-style: italic;
    text-decoration: underline;">Dinosaurs are big.</span>

Internet Explorer:

1
2
<STRONG>I love geckos.</STRONG>
<STRONG><EM><U>Dinosaurs are big.</U></EM></STRONG>

Another difference between Mozilla and IE is how to access the document object of an iframe, which is usually used in conjunction with designMode. Mozilla uses the W3C standard way, namely IFrameElement.contentDocument, while IE requires IFrameElement.document.

DevEdge provides a JavaScript helper class, xbDesignMode, which is a wrapper for the designMode feature which hides the differences between IE and Mozilla.
Event Handling Disabled

A further difference for Mozilla is that once a document is switched to designMode, all events on that particular document are disabled. Once designMode is turned off however (as this now seems possible in Mozilla 1.5) the events become active again.

More information can be found in the Rich text editing section of Migrate apps from Internet Explorer to Mozilla.

Examples

Please note: The example files are still being migrated from another site. They are not available at this time.fumble 18:13, 24 Apr 2005 (PDT)

Example 1

The first example is an HTML document setting its own designMode to “On”. This makes the entire document editable in Mozilla 1.3. Internet Explorer, however, does not allow javascript to change the current document’s designMode. For it to work in Internet Explorer, the contentEditable attribute of the body tag needs to be set to “true”.

Figure 2 : First example

1
2
3
4
5
6
7
HTML:
<body contentEditable="true" onload="load()">
JavaScript:
function load(){
  window.document.designMode = "On";
}

Example 2

The second example is a simple rich text editing page, where text can be bolded/italicized/underlined, new links can be added and the color of text changed. The example page consists of an iframe, which will be the rich editing area, as well as elements for basic editing commands such as bold/italics/text color.

Figure 3 : Setting up rich-text editing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
HTML:
<body onload="load()">
JavaScript:
function load(){
  getIFrameDocument("editorWindow").designMode = "On";
}
function getIFrameDocument(aID){
  // if contentDocument exists, W3C compliant (Mozilla)
  if (document.getElementById(aID).contentDocument){
    return document.getElementById(aID).contentDocument;
  } else {
    // IE
    return document.frames[aID].document;
  }
}

The example contains a doRichEditCommand function that makes it easier to execute commands on the iframe’s document and keeps the HTML code clean. The function executed the requested command using execCommand() and then sets focus back to the editable document, as clicking on a button will set focus on the button itself, which breaks the editing flow.

Figure 4 : Executing Rich Editing Commands

1
2
3
4
5
6
7
8
HTML:
<button onclick="doRichEditCommand('bold')" style="font-weight:bold;">B</button>
JavaScript:
function doRichEditCommand(aName, aArg){
  getIFrameDocument('editorWindow').execCommand(aName,false, aArg);
  document.getElementById('editorWindow').contentWindow.focus()
}

Example: a simple but complete Rich Text Editor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<!doctype html>
<html>
<head>
<title>Rich Text Editor</title>
<script type="text/javascript">
var oDoc, sDefTxt;
function initDoc() {
  oDoc = document.getElementById("textBox");
  sDefTxt = oDoc.innerHTML;
  if (document.compForm.switchMode.checked) { setDocMode(true); }
}
function formatDoc(sCmd, sValue) {
  if (validateMode()) { document.execCommand(sCmd, false, sValue); oDoc.focus(); }
}
function validateMode() {
  if (!document.compForm.switchMode.checked) { return true ; }
  alert("Uncheck \"Show HTML\".");
  oDoc.focus();
  return false;
}
function setDocMode(bToSource) {
  var oContent;
  if (bToSource) {
    oContent = document.createTextNode(oDoc.innerHTML);
    oDoc.innerHTML = "";
    var oPre = document.createElement("pre");
    oDoc.contentEditable = false;
    oPre.id = "sourceText";
    oPre.contentEditable = true;
    oPre.appendChild(oContent);
    oDoc.appendChild(oPre);
  } else {
    if (document.all) {
      oDoc.innerHTML = oDoc.innerText;
    } else {
      oContent = document.createRange();
      oContent.selectNodeContents(oDoc.firstChild);
      oDoc.innerHTML = oContent.toString();
    }
    oDoc.contentEditable = true;
  }
  oDoc.focus();
}
function printDoc() {
  if (!validateMode()) { return; }
  var oPrntWin = window.open("","_blank","width=450,height=470,left=400,top=100,menubar=yes,toolbar=no,location=no,scrollbars=yes");
  oPrntWin.document.open();
  oPrntWin.document.write("<!doctype html><html><head><title>Print<\/title><\/head><body onload=\"print();\">" + oDoc.innerHTML + "<\/body><\/html>");
  oPrntWin.document.close();
}
</script>
<style type="text/css">
.intLink { cursor: pointer; }
img.intLink { border: 0; }
#toolBar1 select { font-size:10px; }
#textBox {
  width: 540px;
  height: 200px;
  border: 1px #000000 solid;
  padding: 12px;
  overflow: scroll;
}
#textBox #sourceText {
  padding: 0;
  margin: 0;
  min-width: 498px;
  min-height: 200px;
}
#editMode label { cursor: pointer; }
</style>
</head>
<body onload="initDoc();">
<form name="compForm" method="post" action="sample.php" onsubmit="if(validateMode()){this.myDoc.value=oDoc.innerHTML;return true;}return false;">
<input type="hidden" name="myDoc">
<div id="toolBar1">
<select onchange="formatDoc('formatblock',this[this.selectedIndex].value);this.selectedIndex=0;">
<option selected>- formatting -</option>
<option value="h1">Title 1 &lt;h1&gt;</option>
<option value="h2">Title 2 &lt;h2&gt;</option>
<option value="h3">Title 3 &lt;h3&gt;</option>
<option value="h4">Title 4 &lt;h4&gt;</option>
<option value="h5">Title 5 &lt;h5&gt;</option>
<option value="h6">Subtitle &lt;h6&gt;</option>
<option value="p">Paragraph &lt;p&gt;</option>
<option value="pre">Preformatted &lt;pre&gt;</option>
</select>
<select onchange="formatDoc('fontname',this[this.selectedIndex].value);this.selectedIndex=0;">
<option class="heading" selected>- font -</option>
<option>Arial</option>
<option>Arial Black</option>
<option>Courier New</option>
<option>Times New Roman</option>
</select>
<select onchange="formatDoc('fontsize',this[this.selectedIndex].value);this.selectedIndex=0;">
<option class="heading" selected>- size -</option>
<option value="1">Very small</option>
<option value="2">A bit small</option>
<option value="3">Normal</option>
<option value="4">Medium-large</option>
<option value="5">Big</option>
<option value="6">Very big</option>
<option value="7">Maximum</option>
</select>
<select onchange="formatDoc('forecolor',this[this.selectedIndex].value);this.selectedIndex=0;">
<option class="heading" selected>- color -</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="black">Black</option>
</select>
<select onchange="formatDoc('backcolor',this[this.selectedIndex].value);this.selectedIndex=0;">
<option class="heading" selected>- background -</option>
<option value="red">Red</option>
<option value="green">Green</option>
<option value="black">Black</option>
</select>
</div>
<div id="toolBar2">
<img class="intLink" title="Clean" onclick="if(validateMode()&&confirm('Are you sure?')){oDoc.innerHTML=sDefTxt};" src="data:image/gif;base64,R0lGODlhFgAWAIQbAD04KTRLYzFRjlldZl9vj1dusY14WYODhpWIbbSVFY6O7IOXw5qbms+wUbCztca0ccS4kdDQjdTLtMrL1O3YitHa7OPcsd/f4PfvrvDv8Pv5xv///////////////////yH5BAEKAB8ALAAAAAAWABYAAAV84CeOZGmeaKqubMteyzK547QoBcFWTm/jgsHq4rhMLoxFIehQQSAWR+Z4IAyaJ0kEgtFoLIzLwRE4oCQWrxoTOTAIhMCZ0tVgMBQKZHAYyFEWEV14eQ8IflhnEHmFDQkAiSkQCI2PDC4QBg+OAJc0ewadNCOgo6anqKkoIQA7" />
<img class="intLink" title="Print" onclick="printDoc();" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9oEBxcZFmGboiwAAAAIdEVYdENvbW1lbnQA9syWvwAAAuFJREFUOMvtlUtsjFEUx//n3nn0YdpBh1abRpt4LFqtqkc3jRKkNEIsiIRIBBEhJJpKlIVo4m1RRMKKjQiRMJRUqUdKPT71qpIpiRKPaqdF55tv5vvusZjQTjOlseUkd3Xu/3dPzusC/22wtu2wRn+jG5So/OCDh8ycMJDflehMlkJkVK7KUYN+ufzA/RttH76zaVocDptRxzQtNi3mRWuPc+6cKtlXZ/sddP2uu9uXlmYXZ6Qm8v4Tz8lhF1H+zDQXt7S8oLMXtbF4e8QaFHjj3kbP2MzkktHpiTjp9VH6iHiA+whtAsX5brpwueMGdONdf/2A4M7ukDs1JW662+XkqTkeUoqjKtOjm2h53YFL15pSJ04Zc94wdtibr26fXlC2mzRvBccEbz2kiRFD414tKMlEZbVGT33+qCoHgha81SWYsew0r1uzfNylmtpx80pngQQ91LwVk2JGvGnfvZG6YcYRAT16GFtW5kKKfo1EQLtfh5Q2etT0BIWF+aitq4fDbk+ImYo1OxvGF03waFJQvBCkvDffRyEtxQiFFYgAZTHS0zwAGD7fG5TNnYNTp8/FzvGwJOfmgG7GOx0SAKKgQgDMgKBI0NJGMEImpGDk5+WACEwEd0ywblhGUZ4Hw5OdUekRBLT7DTgdEgxACsIznx8zpmWh7k4rkpJcuHDxCul6MDsmmBXDlWCH2+XozSgBnzsNCEE4euYV4pwCpsWYPW0UHDYBKSWu1NYjENDReqtKjwn2+zvtTc1vMSTB/mvev/WEYSlASsLimcOhOBJxw+N3aP/SjefNL5GePZmpu4kG7OPr1+tOfPyUu3BecWYKcwQcDFmwFKAUo90fhKDInBCAmvqnyMgqUEagQwCoHBDc1rjv9pIlD8IbVkz6qYViIBQGTJPx4k0XpIgEZoRN1Da0cij4VfR0ta3WvBXH/rjdCufv6R2zPgPH/e4pxSBCpeatqPrjNiso203/5s/zA171Mv8+w1LOAAAAAElFTkSuQmCC">
<img class="intLink" title="Undo" onclick="formatDoc('undo');" src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7/I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq7046807TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfEwhdRIH4fh/DZMICe3/C4nBQBADs=" />
<img class="intLink" title="Redo" onclick="formatDoc('redo');" src="data:image/gif;base64,R0lGODlhFgAWAMIHAB1ChDljwl9vj1iE34Kl8aPD+7/I1////yH5BAEKAAcALAAAAAAWABYAAANKeLrc/jDKSesyphi7SiEgsVXZEATDICqBVJjpqWZt9NaEDNbQK1wCQsxlYnxMAImhyDoFAElJasRRvAZVRqqQXUy7Cgx4TC6bswkAOw==" />
<img class="intLink" title="Remove formatting" onclick="formatDoc('removeFormat')" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB9oECQMCKPI8CIIAAAAIdEVYdENvbW1lbnQA9syWvwAAAuhJREFUOMtjYBgFxAB501ZWBvVaL2nHnlmk6mXCJbF69zU+Hz/9fB5O1lx+bg45qhl8/fYr5it3XrP/YWTUvvvk3VeqGXz70TvbJy8+Wv39+2/Hz19/mGwjZzuTYjALuoBv9jImaXHeyD3H7kU8fPj2ICML8z92dlbtMzdeiG3fco7J08foH1kurkm3E9iw54YvKwuTuom+LPt/BgbWf3//sf37/1/c02cCG1lB8f//f95DZx74MTMzshhoSm6szrQ/a6Ir/Z2RkfEjBxuLYFpDiDi6Af///2ckaHBp7+7wmavP5n76+P2ClrLIYl8H9W36auJCbCxM4szMTJac7Kza////R3H1w2cfWAgafPbqs5g7D95++/P1B4+ECK8tAwMDw/1H7159+/7r7ZcvPz4fOHbzEwMDwx8GBgaGnNatfHZx8zqrJ+4VJBh5CQEGOySEua/v3n7hXmqI8WUGBgYGL3vVG7fuPK3i5GD9/fja7ZsMDAzMG/Ze52mZeSj4yu1XEq/ff7W5dvfVAS1lsXc4Db7z8C3r8p7Qjf///2dnZGxlqJuyr3rPqQd/Hhyu7oSpYWScylDQsd3kzvnH738wMDzj5GBN1VIWW4c3KDon7VOvm7S3paB9u5qsU5/x5KUnlY+eexQbkLNsErK61+++VnAJcfkyMTIwffj0QwZbJDKjcETs1Y8evyd48toz8y/ffzv//vPP4veffxpX77z6l5JewHPu8MqTDAwMDLzyrjb/mZm0JcT5Lj+89+Ybm6zz95oMh7s4XbygN3Sluq4Mj5K8iKMgP4f0////fv77//8nLy+7MCcXmyYDAwODS9jM9tcvPypd35pne3ljdjvj26+H2dhYpuENikgfvQeXNmSl3tqepxXsqhXPyc666s+fv1fMdKR3TK72zpix8nTc7bdfhfkEeVbC9KhbK/9iYWHiErbu6MWbY/7//8/4//9/pgOnH6jGVazvFDRtq2VgiBIZrUTIBgCk+ivHvuEKwAAAAABJRU5ErkJggg==">
<img class="intLink" title="Bold" onclick="formatDoc('bold');" src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAInhI+pa+H9mJy0LhdgtrxzDG5WGFVk6aXqyk6Y9kXvKKNuLbb6zgMFADs=" />
<img class="intLink" title="Italic" onclick="formatDoc('italic');" src="data:image/gif;base64,R0lGODlhFgAWAKEDAAAAAF9vj5WIbf///yH5BAEAAAMALAAAAAAWABYAAAIjnI+py+0Po5x0gXvruEKHrF2BB1YiCWgbMFIYpsbyTNd2UwAAOw==" />
<img class="intLink" title="Underline" onclick="formatDoc('underline');" src="data:image/gif;base64,R0lGODlhFgAWAKECAAAAAF9vj////////yH5BAEAAAIALAAAAAAWABYAAAIrlI+py+0Po5zUgAsEzvEeL4Ea15EiJJ5PSqJmuwKBEKgxVuXWtun+DwxCCgA7" />
<img class="intLink" title="Left align" onclick="formatDoc('justifyleft');" src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIghI+py+0Po5y02ouz3jL4D4JMGELkGYxo+qzl4nKyXAAAOw==" />
<img class="intLink" title="Center align" onclick="formatDoc('justifycenter');" src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIfhI+py+0Po5y02ouz3jL4D4JOGI7kaZ5Bqn4sycVbAQA7" />
<img class="intLink" title="Right align" onclick="formatDoc('justifyright');" src="data:image/gif;base64,R0lGODlhFgAWAID/AMDAwAAAACH5BAEAAAAALAAAAAAWABYAQAIghI+py+0Po5y02ouz3jL4D4JQGDLkGYxouqzl43JyVgAAOw==" />
<img class="intLink" title="Numbered list" onclick="formatDoc('insertorderedlist');" src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAADljwliE35GjuaezxtHa7P///////yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKSespwjoRFvggCBUBoTFBeq6QIAysQnRHaEOzyaZ07Lu9lUBnC0UGQU1K52s6n5oEADs=" />
<img class="intLink" title="Dotted list" onclick="formatDoc('insertunorderedlist');" src="data:image/gif;base64,R0lGODlhFgAWAMIGAAAAAB1ChF9vj1iE33mOrqezxv///////yH5BAEAAAcALAAAAAAWABYAAAMyeLrc/jDKSesppNhGRlBAKIZRERBbqm6YtnbfMY7lud64UwiuKnigGQliQuWOyKQykgAAOw==" />
<img class="intLink" title="Quote" onclick="formatDoc('formatblock','blockquote');" src="data:image/gif;base64,R0lGODlhFgAWAIQXAC1NqjFRjkBgmT9nqUJnsk9xrFJ7u2R9qmKBt1iGzHmOrm6Sz4OXw3Odz4Cl2ZSnw6KxyqO306K63bG70bTB0rDI3bvI4P///////////////////////////////////yH5BAEKAB8ALAAAAAAWABYAAAVP4CeOZGmeaKqubEs2CekkErvEI1zZuOgYFlakECEZFi0GgTGKEBATFmJAVXweVOoKEQgABB9IQDCmrLpjETrQQlhHjINrTq/b7/i8fp8PAQA7" />
<img class="intLink" title="Add indentation" onclick="formatDoc('outdent');" src="data:image/gif;base64,R0lGODlhFgAWAMIHAAAAADljwliE35GjuaezxtDV3NHa7P///yH5BAEAAAcALAAAAAAWABYAAAM2eLrc/jDKCQG9F2i7u8agQgyK1z2EIBil+TWqEMxhMczsYVJ3e4ahk+sFnAgtxSQDqWw6n5cEADs=" />
<img class="intLink" title="Delete indentation" onclick="formatDoc('indent');" src="data:image/gif;base64,R0lGODlhFgAWAOMIAAAAADljwl9vj1iE35GjuaezxtDV3NHa7P///////////////////////////////yH5BAEAAAgALAAAAAAWABYAAAQ7EMlJq704650B/x8gemMpgugwHJNZXodKsO5oqUOgo5KhBwWESyMQsCRDHu9VOyk5TM9zSpFSr9gsJwIAOw==" />
<img class="intLink" title="Hyperlink" onclick="var sLnk=prompt('Write the URL here','http:\/\/');if(sLnk&&sLnk!=''&&sLnk!='http://'){formatDoc('createlink',sLnk)}" src="data:image/gif;base64,R0lGODlhFgAWAOMKAB1ChDRLY19vj3mOrpGjuaezxrCztb/I19Ha7Pv8/f///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARY8MlJq7046827/2BYIQVhHg9pEgVGIklyDEUBy/RlE4FQF4dCj2AQXAiJQDCWQCAEBwIioEMQBgSAFhDAGghGi9XgHAhMNoSZgJkJei33UESv2+/4vD4TAQA7" />
<img class="intLink" title="Cut" onclick="formatDoc('cut');" src="data:image/gif;base64,R0lGODlhFgAWAIQSAB1ChBFNsRJTySJYwjljwkxwl19vj1dusYODhl6MnHmOrpqbmpGjuaezxrCztcDCxL/I18rL1P///////////////////////////////////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAVu4CeOZGmeaKqubDs6TNnEbGNApNG0kbGMi5trwcA9GArXh+FAfBAw5UexUDAQESkRsfhJPwaH4YsEGAAJGisRGAQY7UCC9ZAXBB+74LGCRxIEHwAHdWooDgGJcwpxDisQBQRjIgkDCVlfmZqbmiEAOw==" />
<img class="intLink" title="Copy" onclick="formatDoc('copy');" src="data:image/gif;base64,R0lGODlhFgAWAIQcAB1ChBFNsTRLYyJYwjljwl9vj1iE31iGzF6MnHWX9HOdz5GjuYCl2YKl8ZOt4qezxqK63aK/9KPD+7DI3b/I17LM/MrL1MLY9NHa7OPs++bx/Pv8/f///////////////yH5BAEAAB8ALAAAAAAWABYAAAWG4CeOZGmeaKqubOum1SQ/kPVOW749BeVSus2CgrCxHptLBbOQxCSNCCaF1GUqwQbBd0JGJAyGJJiobE+LnCaDcXAaEoxhQACgNw0FQx9kP+wmaRgYFBQNeAoGihCAJQsCkJAKOhgXEw8BLQYciooHf5o7EA+kC40qBKkAAAGrpy+wsbKzIiEAOw==" />
<img class="intLink" title="Paste" onclick="formatDoc('paste');" src="data:image/gif;base64,R0lGODlhFgAWAIQUAD04KTRLY2tXQF9vj414WZWIbXmOrpqbmpGjudClFaezxsa0cb/I1+3YitHa7PrkIPHvbuPs+/fvrvv8/f///////////////////////////////////////////////yH5BAEAAB8ALAAAAAAWABYAAAWN4CeOZGmeaKqubGsusPvBSyFJjVDs6nJLB0khR4AkBCmfsCGBQAoCwjF5gwquVykSFbwZE+AwIBV0GhFog2EwIDchjwRiQo9E2Fx4XD5R+B0DDAEnBXBhBhN2DgwDAQFjJYVhCQYRfgoIDGiQJAWTCQMRiwwMfgicnVcAAAMOaK+bLAOrtLUyt7i5uiUhADs=" />
</div>
<div id="textBox" contenteditable="true"><p>Lorem ipsum</p></div>
<p id="editMode"><input type="checkbox" name="switchMode" id="switchBox" onchange="setDocMode(this.checked);" /> <label for="switchBox">Show HTML</label></p>
<p><input type="submit" value="Send" /></p>
</form>
</body>
</html>
By Rz Rasel Posted in jQuery

The difference between setTimeout and setInterval

setTimeout: Run once and then stop.

Code sample: var timerA = setTimeout(“AFunction()”,1000);

How to loop: function AFunction(){/*..Some codes..*/ timerA = setTimeout(“AFunction()”,1000);}

How to stop: clearTimeout(timerA);

Example: Tue Aug 07 2012 11:28:42 GMT+0600 (Central Asia Standard Time)

Example Codes:
<i id=”lblstdate”>timer A</i>
<script type=”text/javascript”>
function AFunction(){
document.getElementById(‘lblstdate’).innerHTML=(new Date()).toString();
setTimeout(“AFunction()”,1000)
}
AFunction();
</script>


setInterval: Run by loop.

Code sample: var timerB = setInterval(“BFunction()”,1000);

How to loop: It loops automatically.

How to stop: clearInterval(timerB);

Example: Tue Aug 07 2012 11:28:42 GMT+0600 (Central Asia Standard Time)

Example codes:
<i id=”lblsidate”>timer B</i>
<script type=”text/javascript”>
function BFunction(){
document.getElementById(‘lblsidate’).innerHTML=(new Date()).toString();
}
var timerB = setInterval(“BFunction()”,1000);
//clearInterval(timerB)
</script>
Interval will run in an interval, meaning over and over again. setTimeout will run only once.

By Rz Rasel Posted in jQuery

Print The Contents Of A Div

<html>
<head>
<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.1.min.js" > </script> 
<script type="text/javascript">

    function PrintElem(elem)
    {
        Popup($(elem).text());
    }

    function Popup(data) 
    {
        var mywindow = window.open('', 'my div', 'height=400,width=600');
        mywindow.document.write('<html><head><title>my div</title>');
        /*optional stylesheet*/ //mywindow.document.write('<link rel="stylesheet" href="main.css" type="text/css" />');
        mywindow.document.write('</head><body >');
        mywindow.document.write(data);
        mywindow.document.write('</body></html>');

        mywindow.print();
        mywindow.close();

        return true;
    }

</script>
</head>
<body>

<div id="mydiv">
    This will be printed. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque a quam at nibh adipiscing interdum. Nulla vitae accumsan ante. 
</div>

<div>
    This will not be printed.
</div>

<div id="anotherdiv">
    Nor will this.
</div>

<input type="button" value="Print Div" onclick="PrintElem('#mydiv')" />

</body>
</html>
By Rz Rasel Posted in jQuery

How To Print Part Of Rendered Html Page In Javascript?

<html>
<head>
<title>Print Test Page</title>
<script>
printDivCSS = new String ('<link href="myprintstyle.css" rel="stylesheet" type="text/css">')
function printDiv(divId) {
    window.frames["print_frame"].document.body.innerHTML=printDivCSS + document.getElementById(divId).innerHTML
    window.frames["print_frame"].window.focus()
    window.frames["print_frame"].window.print()
}
</script>
</head>
<body>
<h1><b><center>This is a test page for printing</center></b><hr color=#00cc00 width=95%></h1>
<b>Div 1:</b> <a href=javascript:printDiv('div1')>Print</a><br>
<div id=div1>This is the div1's print output</div>
<br><br>
<b>Div 2:</b> <a href=javascript:printDiv('div2')>Print</a><br>
<div id=div2>This is the div2's print output</div>
<br><br>
<b>Div 3:</b> <a href=javascript:printDiv('div3')>Print</a><br>
<div id=div2>This is the div3's print output</div>

</body>
</html>
By Rz Rasel Posted in jQuery

Print Part Of A Web Page With jQuery

To be honest, I’ve never done this before, so I am not sure if the following solution is truly cross browser compliant. The jQuery plugin that I authored below was tested to be working in FireFox, IE 7, Safari, and the latest version of Chrome Beta (although Chrome seemed to have some issues with the images from time to time). Before we get into how it works, take a look at this demo video so you can see what I’m talking about:

As you can see, I created a jQuery plugin to handle the print() functionality. This way, we can make anything on the page printable using a jQuery selector. The caveat being that the containing element is not printed – only it’s child elements get added to the print document. In the following page, when the DOM is ready, we find our link element and have it print (upon click) any element on the page with the class “printable:”

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
  • <html>
  • <head>
  • <title>Print Part of a Page With jQuery</title>
  • <script type=”text/javascript” src=”jquery-1.3.2.js”></script>
  • <script type=”text/javascript” src=”jquery.print.js”></script>
  • <script type=”text/javascript”>
  • // When the document is ready, initialize the link so
  • // that when it is clicked, the printable area of the
  • // page will print.
  • $(
  • function(){
  • // Hook up the print link.
  • $( “a” )
  • .attr( “href”, “javascript:void( 0 )” )
  • .click(
  • function(){
  • // Print the DIV.
  • $( “.printable” ).print();
  • // Cancel click event.
  • return( false );
  • }
  • )
  • ;
  • }
  • );
  • </script>
  • <style type=”text/css”>
  • body {
  • font-family: verdana ;
  • font-size: 14px ;
  • }
  • h1 {
  • font-size: 180% ;
  • }
  • h2 {
  • border-bottom: 1px solid #999999 ;
  • }
  • .printable {
  • border: 1px dotted #CCCCCC ;
  • padding: 10px 10px 10px 10px ;
  • }
  • img {
  • background-color: #E0E0E0 ;
  • border: 1px solid #666666 ;
  • padding: 5px 5px 5px 5px ;
  • }
  • a {
  • color: red ;
  • }
  • </style>
  • </head>
  • <body>
  • <h1>
  • Print Part of a Page With jQuery
  • </h1>
  • <p>
  • <a>Print Bio</a>
  • </p>
  • <div class=”printable”>
  • <h2>
  • Jen Rish
  • </h2>
  • <p>
  • Jen Rish, upcoming fitness and figure model has some
  • crazy developed legs!
  • </p>
  • <p>
  • <img
  • src=”jen_rish_crazy_legs.jpg”
  • width=”380″
  • height=”570″
  • alt=”Jen Rish Has Amazing Legs!”
  • />
  • </p>
  • <p>
  • I bet she does some <strong>serious squatting</strong>!
  • </p>
  • </div>
  • </body>
  • </html>

And, here is the jQuery plugin, print(), that powers this solution:

 Launch code in new window » Download code as text file »

  • // Create a jquery plugin that prints the given element.
  • jQuery.fn.print = function(){
  • // NOTE: We are trimming the jQuery collection down to the
  • // first element in the collection.
  • if (this.size() > 1){
  • this.eq( 0 ).print();
  • return;
  • } else if (!this.size()){
  • return;
  • }
  • // ASSERT: At this point, we know that the current jQuery
  • // collection (as defined by THIS), contains only one
  • // printable element.
  • // Create a random name for the print frame.
  • var strFrameName = (“printer-” + (new Date()).getTime());
  • // Create an iFrame with the new name.
  • var jFrame = $( “<iframe name='” + strFrameName + “‘>” );
  • // Hide the frame (sort of) and attach to the body.
  • jFrame
  • .css( “width”, “1px” )
  • .css( “height”, “1px” )
  • .css( “position”, “absolute” )
  • .css( “left”, “-9999px” )
  • .appendTo( $( “body:first” ) )
  • ;
  • // Get a FRAMES reference to the new frame.
  • var objFrame = window.frames[ strFrameName ];
  • // Get a reference to the DOM in the new frame.
  • var objDoc = objFrame.document;
  • // Grab all the style tags and copy to the new
  • // document so that we capture look and feel of
  • // the current document.
  • // Create a temp document DIV to hold the style tags.
  • // This is the only way I could find to get the style
  • // tags into IE.
  • var jStyleDiv = $( “<div>” ).append(
  • $( “style” ).clone()
  • );
  • // Write the HTML for the document. In this, we will
  • // write out the HTML of the current element.
  • objDoc.open();
  • objDoc.write( “<!DOCTYPE html PUBLIC \”-//W3C//DTD XHTML 1.0 Transitional//EN\” \”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\”>” );
  • objDoc.write( “<html>” );
  • objDoc.write( “<body>” );
  • objDoc.write( “<head>” );
  • objDoc.write( “<title>” );
  • objDoc.write( document.title );
  • objDoc.write( “</title>” );
  • objDoc.write( jStyleDiv.html() );
  • objDoc.write( “</head>” );
  • objDoc.write( this.html() );
  • objDoc.write( “</body>” );
  • objDoc.write( “</html>” );
  • objDoc.close();
  • // Print the document.
  • objFrame.focus();
  • objFrame.print();
  • // Have the frame remove itself in about a minute so that
  • // we don’t build up too many of these frames.
  • setTimeout(
  • function(){
  • jFrame.remove();
  • },
  • (60 * 1000)
  • );
  • }

The plugin itself is not too complicated – we are creating an IFrame on the fly, writing the target HTML to its body, and then printing it. It’s a simple concept, but there were some issues getting it to work in Internet Explorer (IE). The following caveats tripped me up at first:

  • In IE, you have to focus() the IFrame before you print it other wise the top page prints.
  • In IE, I kept getting errors when trying to use jQuery to write the STYLE tags to the IFrame head. As such, I had to write the html() of the STYLE tags as I was writing out the document. The Style tags are crucial to in the document because they will determine the look and feel of the printed content.

Once I got those two issues out of the way, the rest worked pretty smoothly. I am not sure if this is the best technique, but hopefully it will point you in the right direction.

Download Code Snippet ZIP File

By Rz Rasel Posted in jQuery

Dynamic Go-to-Top Link In jQuery

Instead of displaying a perpetual “go to top” link for every page, here’s a quick way to dynamically display the link only if the content height is greater than that of the browser window. So it’s there when you need it, not when you don’t. Just include the following snippet of jQuery with your design:

// dynamic top link @ http://perishablepress.com/dynamic-top-link/
function xycss_dynamic_top_link(){
	if($(window).height() < $(document).height()) {
		$('body').append('<div id="top-link">&uarr;</div>');
		$('#top-link').click(function() {
			window.location = "#top";
		}).css({
			width:'24px', height:'24px', lineHeight:'24px', bottom:'3px', right:'3px', 
			background:'#663300', color:'#fff', opacity:0.3, zIndex:999, textAlign:'center', 
			textDecoration:'none', position:'fixed', cursor:'pointer',
			border:'3px solid #c7bc8e', borderRadius:'24px'
		}).hover(function(){
			$(this).css('opacity',0.6);
		},function(){
			$(this).css('opacity',0.3);
		});
	}
}
$(document).ready(function(){
	xycss_dynamic_top_link();
});
$(window).resize(function() {
	$('#top-link').remove();
	xycss_dynamic_top_link();
});

That’s pretty much it, should be plug-n-play. I’ve included the same CSS styles I use here at the site, for the circle with the “up” arrow (see lower-right corner of the page). When the page content exceeds the height of the browser window, the top link is included using the following markup:

<div id="top-link">&uarr;</div>

This markup will serve as a “clickable” link that will take the user to the top of the page. So you can style it directly with #button and/or #top-link. See the various .css() parts of the code to change the link’s appearance and so forth.

And of course if the user doesn’t have JavaScript, they’ll can just use their browser’s default scrollbars, swipes, or whatever.

By Rz Rasel Posted in jQuery

Tutorial: Object-Oriented Way of Creating a jQuery Image Slider

Table of Contents:

The purpose of this article is to demonstrate object-oriented design in JavaScript, by creating a jQuery powered image gallery script from scratch. Note that we are not creating a jQuery plugin, but simply using jQuery for DOM selection and manipulation. In the near future, this article might be updated to jQuery plugin version.

 

Resources

Demonstration: http://test.thecodecentral.com/demos/simple_gallery/
See it on GitHub: https://github.com/thecodecentral/Simple-Image-Slider

The Theory

The theory behind it is pretty simple. We first align a list of images side by side, this will allow us to create a long horizontal panel. Above the panel, we create a “windowed” area to show only part of the panel. We achieve this by setting style of the container element to overflow:none. Lastly, we implement JavaScript code to move the horizontal panel.

Creating the Object Blueprint

There’s no such thing really is class in JavaScript, so we will call it object blueprint for now. But for convenience, we will loosely refer this as class later on. We are going to create something like this,

  1. var SimpleGallery = function(){
  2.   //constructor
  3. };
  4. SimpleGallery.prototype = {
  5.   //implementation
  6. };

Once we have the blue print, we can create the gallery object like this,

  1. var gallery = new SimpleGallery();

If you are curious about how it works, here is a good read about applying OOP paradigm in JavaScript.

HTML/CSS Prototype

Before implementing the JavaScript further, we are going to create a quick prototype of the gallery. No JavaScript is involved in this step.

  1. <html>
  2.   <head>
  3.     <style type=”text/css”>
  4.       .simple_gallery{
  5.         width: 700px;
  6.         height: 400px;
  7.         position: relative;
  8.         overflow: hidden;
  9.       }
  10.       .simple_gallery .slide_panel{
  11.         position: absolute;
  12.         clear: both;
  13.         height: 400px;
  14.         width: 10000px;
  15.         left: 0;
  16.         top: 0;
  17.       }
  18.     </style>
  19.   </head>
  20.   <body>
  21.     <div id=”gallery” class=”simple_gallery” style=”display: none”>
  22.       <div class=”slide_panel”>
  23.         <img src=”images/1.jpg” alt=”Image 1″/>
  24.         <img src=”images/2.jpg” alt=”Image 2″/>
  25.         <img src=”images/3.jpg” alt=”Image 3″/>
  26.         <img src=”images/4.jpg” alt=”Image 4″/>
  27.         <img src=”images/5.jpg” alt=”Image 5″/>
  28.         <img src=”images/6.jpg” alt=”Image 6″/>
  29.         <img src=”images/7.jpg” alt=”Image 7″/>
  30.       </div>
  31.     </div>
  32.   </body>
  33. </html>

By this little amount of code, we already have a bare-bone version of the gallery we are trying to create. DIV.slide_panel is now scrollable. If you have Firefox browser with Firebug installed, you can scroll it by editing the left position of this DIV element. See a live demonstration here.

We are going to expand our JavaScript code based on this prototype gallery.

Coding the Gallery Script

Firstly, please have a look at the completed script to get an overview of what it looks like. The entire script seems long, but it is divided into many well defined code blocks, which can help you to understand the code better. The constructor now has been updated to initialize many object wide variables. You don’t need to know what these are, but just remember they will be used in some places within the class.

  1. //constrcutor
  2. var SimpleGallery = function(options){
  3.   this.renderToEl = null;
  4.   this.options = $.extend({
  5.     slidePanelClassName: ‘slide_panel’,
  6.     navLeftClassName: ‘nav_left’,
  7.     navRightClassName: ‘nav_right’,
  8.     slideClassName: ‘slide_block’, //class to determine the width of each slide,
  9.     itemRenderer: this.itemRenderer,
  10.     galleryRenderer: this.galleryRenderer
  11.   }, options);
  12.   //so on …
  13.   //see simple.gallery.js
  14. };

One thing may be confusing to new jQuery user is the using of $.extend. Here we use $.extend to merge options passed to the constructor with the default options inside of the constructor. For example, if we do

  1. new SimpleGallery({slideClassName: ‘slide_new’});

this.options.slideClassName will become ‘slide_new’, instead of the old value ‘slide_block’.

The rest is the class body, which are merely functions.

  1. SimpleGallery.prototype = {
  2.  getData:function(){
  3.   },
  4.   getCurrentSlide:function(){
  5.   },
  6.   getNumberSlides:function(){
  7.   },
  8.   getCurrentSlideData:function(){
  9.   },
  10.   render:function(toEl){
  11.   },
  12.   scrollTo: function(index){
  13.   }
  14.   //and so on …
  15. }

The Scroll to Function

I will focus on discussing the “scrollTo” function, which is the most important function in the class. This function is not hard to understand either. You can almost read it as plain English. The function take a parameter “index”, which is the slide number to scroll to. Please read the code comment for more explanations.

  1. /**
  2.  * Scroll to a slide. Index is 0 based.
  3.  */
  4. scrollTo: function(index){
  5.   //if it is scrolling, do nothing
  6.   if(this.isAnimating){
  7.     return;
  8.   }
  9.   //if index number is out of bound, i.e., invalid, do nothing
  10.   if(index < 0 || index >= this.numberSlides){
  11.     return;
  12.   }
  13.   //if index is the current slide, do thing
  14.   if(index == this.currentSlide){
  15.     return;
  16.   }
  17.   //if we can get to here, scroll to the appropriate slide
  18.   this.isAnimating = true;
  19.   this.slidePanel.show();
  20.   //store “this” context, in order to use it in the animation callback
  21.   var self = this;
  22.   //hide navigations first, then show after animation is completed.
  23.   this.navLeft.hide();
  24.   this.navRight.hide();
  25.   //finally, scroll to the image panel
  26.   this.slidePanel.animate({
  27.     left: (-(index * this.slideWidth)) + ‘px’
  28.   }, ‘slow’, function(){
  29.     //I can’t use this.currentSlide to access class variables, because I am inside of a callback function.
  30.     //”this” in this scope refers to this.slidePanel
  31.     //”self” refers to class instance
  32.     //so to access class variable, I use “self”, which is defined a few lines above
  33.     //we have scrolled to the designated slide, update current slide index
  34.     self.currentSlide = index;
  35.     //indicate that animation is done
  36.     self.isAnimating = false;
  37.     //handle how to display backward/forward button.
  38.     //For example, at the first slide, backward button is hidden; at last page, forward button is hidden.
  39.     self.showHideNav();
  40.     //to make this class even more customizable, we trigger a event
  41.     //when the animation is completed. you can fire more interest events.
  42.     //For example, when at first slide, trigger a ‘simple.gallery.at_first_slide’ event.
  43.     self.renderToEl.trigger(‘simple.gallery.animation_completed’);
  44.   });
  45. }

Once the scrollTo function is implemented, we just need to add navigation buttons, then we have an image slider. “setupNav” and “showHideNav” functions will take care adding and displaying the navigation buttons.

The render() Function

The “render()” function is responsible to display the image slider. We are going to go over this function shortly, but now, just have a quick look at the code.

  1. /**
  2.  * render gallery to the targeted jQuery element
  3.  * @param toEl jQuery element
  4.  */
  5. render:function(toEl){
  6.   this.renderToEl = toEl;
  7.   this.renderToEl.append(this.options.galleryRenderer.apply(this, [this]));
  8.   var panel = toEl.find(‘.’ + this.options.slidePanelClassName);
  9.   panel.empty();
  10.   for(var i in this.data){
  11.     var slide = this.options.itemRenderer.apply(this, [this.data[i]]);
  12.     panel.append(slide);
  13.   }
  14.   this.slidePanel = panel;
  15.   this.slideWidth = panel.find(‘.’ + this.options.slideClassName).width();
  16.   panel.width(this.numberSlides *  this.slideWidth);
  17.   panel.show();
  18.   this.setupNav();
  19.   this.scrollTo(0);
  20. }

It take an argument, which is an jQuery element that you want the slider to be appended into. This function should be pretty easy to understand, except two places:

  1. this.options.galleryRenderer.apply(this, [this])
  2. this.options.itemRenderer.apply(this, [this.data[i]])

Above code is roughly the same as below,

  1. this.options.galleryRenderer(this)
  2. this.options.itemRenderer(this.data[i])

Then, you might ask, why would I use “apply” instead of calling “galleryRenderer” and “itemRenderer” directly? To answer this question, we need to understand what “apply” does. If you are not familiar with the scope problem in JavaScript, here is a very good read. So basically “apply” will correct the scope of two renderers functions (see below for explanation of what renderer is). Still remember this.options = $.extend({}, options) in the constructor? After that call, this.options is assigned to an object. So calling this.options.galleryRenderer(this), will be in the scope of “options” object, not the SimpleGallery object; but this is not what we want. We use apply to fix this problem.

Notice that I put two render functions in the this.options variable. This will provide maximum flexibility. This way, either renderer can be overwritten and customized. Please see final demonstration page for how to customize the renderer.

Instantiating the Slider Object (Directly)

Once the slider class is done, we can instantiate it. We can do it programmatically like this,

  1. var galleryData = [
  2. { url=”images/1.jpg”, title=”Image 1″, description=”Description of the image 1″},
  3. { url=”images/2.jpg”, title=”Image 2″, description=”Description of the image 2″},
  4. { url=”images/3.jpg”, title=”Image 3″, description=”Description of the image 3″},
  5. { url=”images/4.jpg”, title=”Image 4″, description=”Description of the image 4″},
  6. { url=”images/5.jpg”, title=”Image 5″, description=”Description of the image 5″},
  7. { url=”images/6.jpg”, title=”Image 6″, description=”Description of the image 6″},
  8. { url=”images/7.jpg”, title=”Image 7″, description=”Description of the image 7″}
  9. ];
  10. var gallery = new SimpleGallery();
  11. gallery.setData(galleryData).render($(‘#gallery’));

Instantiating the Slider Object (With a Crawler)

Better yet, we can create a slider based on existing images in the document. I have created a simple image crawler, which can extract image information and construct data for the slider class.

  1. /**
  2.  * Select all images, turn them into list of
  3.  * @param el jQuery element
  4.  **/
  5. var SimpleGalleryCrawlerGeneric = function(el){
  6.   this.el = el;
  7. };
  8. SimpleGalleryCrawlerGeneric.prototype = {
  9.   /**
  10.    * extract images from the given element
  11.    * @param el jQuery element
  12.    * @return image URLs and other information
  13.    */
  14.   getData:function(){
  15.     var data = [];
  16.     var self = this;
  17.     this.el.find(‘img’).each(function(i){
  18.       var img = $(this);
  19.       var item = {
  20.         ‘url’:img.attr(‘src’),
  21.         ‘title’:img.attr(‘alt’),
  22.         ‘description’: img.attr(‘title’)
  23.       };
  24.       data.push(item);
  25.     });
  26.     return data;
  27.   }
  28. };

Now, let’s say the original page contains the following,

  1.  <ul id=”gallery_data”>
  2.       <li><img src=”images/1.jpg” alt=”Image 1″ title=”Description of the image 1″/></li>
  3.       <li><img src=”images/2.jpg” alt=”Image 2″ title=”Description of the image 2″/></li>
  4.       <li><img src=”images/3.jpg” alt=”Image 3″ title=”Description of the image 3″/></li>
  5.       <li><img src=”images/4.jpg” alt=”Image 4″ title=”Description of the image 4″/></li>
  6.       <li><img src=”images/5.jpg” alt=”Image 5″ title=”Description of the image 5″/></li>
  7.       <li><img src=”images/6.jpg” alt=”Image 6″ title=”Description of the image 6″/></li>
  8.       <li><img src=”images/7.jpg” alt=”Image 7″ title=”Description of the image 7″/></li>
  9. </ul>

The image slider can be created like this,

  1. $(‘#gallery_data’).hide();
  2. var galleryData = new SimpleGalleryCrawlerGeneric($(‘#gallery_data’)).getData();
  3. var gallery = new SimpleGallery();
  4. gallery.setData(galleryData).render($(‘#gallery’));

This will create a good fall back mechanism. That is, when JavaScript is not supported, original images will be displayed. If JavaScript is supported, slider is displayed. Take a look the demo page, and try to disable JavaScript to see how it falls back nicely.

The Completed Code

JavaScript: http://test.thecodecentral.com/demos/simple_gallery/lib/simple.gallery.js
Slider Demo: http://test.thecodecentral.com/demos/simple_gallery/

By Rz Rasel Posted in jQuery

A Simple, Powerful, Lightweight Class for jQuery

jQuery’s functional programming style is downright elegant for manipulating the DOM. But it lacks the structure and code reuse patterns that professional scripters need. John Resig’s Simple Class inspired, jQuery.Class is a lightweight, but powerful class-like system that bridges the gap between jQuery’s functional programming style and Object Oriented Programming.

Features

  • Static and prototypal inheritance
  • Introspection
  • Namespaces
  • Setup and initialization methods
  • Easy callback creation

Download

jquery.class.js (minimized 2kb)

Demo

Extend a tabs widget to a history tabs widget demo.

Documentation

JavaScriptMVC’s Class docs.

Use

To create a monster class with static and prototype properties:

$.Class("Monster",
// static properties
{
  count: 0  
},
// prototype properties
{
  // constructor function
  init : function(name){
    //save the name
    this.name = name;

    this.energy = 10;

    //increment the static count
    this.Class.count++;
  }
})

//create a monster
var dragon = new Monster('dragon');

Inheritance

To extend (or inherit) from a class just call .extend on the class and provide the new name of your class and the static and prototype properties:

Monster("SeaMonster",{},{
  swim : function(distance){
    this.energy -= distance
  }
})

If you want to call an base class’s static or prototype method from the extended class:

SeaMonster("LochNessMonster",{},{
  swim : function(distance){
    this._super(distance/2);
  }
})

Notice how _super calls the base class’s method.

Introspection

You can access the full and short name of the class for groovy introspection techniques. The following uses the class’s name to change where the ajax request is sent:

$.Class("Model",{
  findAll : function(success){
    // use the class's name to find items
    $.get('/'+this.shortName.toLowerCase()+'s',
           success,
           'json')
  }
},{});

// create a task model
Model('Task')

//use it to make a request to /tasks
Task.findAll(function(tasks){
   alert(tasks.length);
})

Namespaces

Namespaces are a GREAT idea. It’s nice when code doesn’t clobber each other.

$.Class("MyCo.Project.MyClass");

MyCo.Project.MyClass.shortName //-> MyClass

MyCo.Project.MyClass.fullName //-> MyCo.Project.MyClass

Setup and Initialization Methods

When a class or instance of a class is created, setup and init methods are called on the class or instance. Setupis called before init and can be used to normalize arguments to init. Initis the class or instance’s constructor function. But unlike normal constructor functions, you don’t have to worry initialization code that will prevent further inheritance*.

The following makes sure all classes extending Controller will have their init function called with a jQuery element instead of an element:

$.Class("Controller",{
  setup : function(el){
    arguments[0] = jQuery(el);
    return arguments;
  }
})

// A dummy tabs class
Controller('Tabs',{
  init : function(el){
    this.element = el;
  }
})

var tab1 = new Tabs($("#foo")),
    tab2 = new Tabs($("#bar")[0]);

//call jQuery methods on element
tab1.element.show();
tab2.element.hide();

*You probably won’t need setup methods, but it’s cool if you need a lot of pre-processing for extending classes. The problem with normal constructor functions is that they typically need to be callable without arguments to be able to setup a correct prototype chain.

Easy Callback Creation

Similar to jQuery’s proxy, Class provides a static and prototype callback function that returns a callback that has ‘this’ or the context set to the Class or instance.

The following uses callback to return a function that will call the new task’s show method with ‘this’ set to the new task.

$.Class("Task",{
  init : function(name){
    this.name = name
  },
  get : function(){
    $.get('task',
         {name:this.name},
         this.callback('show'),
         'text')
  },
  show : function(instructions){
    alert(this.name+instructions)
  }
})

new Task("dishes").get()

The callback function lets you curry arguments and even gets a little Aspect Oriented Program-ish and lets you chain multiple callbacks*:

$.Class("Task",{
  init : function(name){
    this.name = name
  },
  get : function(date, callback){
    $.get('task',
         {name:this.name},

         // call show, then callback, curry date
         this.callback(['show', callback], date),
         'text')
  },
  show : function(date, instructions){
    // return arguments for next function
    return [this.name, date, instructions]
  }
})

new Task("dishes").get(
  new Date(), 
  function(name, date, instructions){
    console.log(name, date, instructions);
  }
)

*Chaining callbacks is helpful when there are multiple processing steps before some final callback is called. It makes it so you don’t have to curry the final callback over and over again. ;

Conclusions

Use classes where appropriate. There has been some fun poked at classy-ing up jQuery. But there is an important difference between an API for modifying the DOM and a sortable-paginated-groupable grid.

jQuery lacks a clear and repeatable pattern for organizing code. It also lacks tools to extend and add functionality to existing widgets. This is to jQuery’s strength. It makes jQuery useful to everyone.

But, as code gets more complex and teams get bigger, the need for patterns and tools to bridge the jQuery to Object Oriented Architecture gap becomes unavoidable. We hope jQuery.Class can help.

By Rz Rasel Posted in jQuery

jQuery Object Oriented Plugins

Many people have asked me, if jQuery is object oriented and how they can use it same way as they use PHP or JAVA. For example:

object1 = new Object();

object1.testMethod();

As it appears you can do that too in a jQuery if plugin is properly developed. Of course the nature of the Javascript language is not OOP. Javascript is prototyping language but not OOP. However u can prototype properties and methods to make it look like its OOP.

In jQuery I have created a test plugin for you so it is easier to understand.

<script type="text/javascript">// <![CDATA[
//Lets consider it as our class wrapper
(function( $ ) {

	$.fn.testClass = function( initvar ) {

		this.testMethod = function(status) {
			this.test = status;

			//lets call a method of a class
			this.anotherMethod();
		};

		this.anotherMethod = function() {
			alert("anotherMethod is called by object " + this.test);
		};

		this.getVars = function() {
			alert("Class var set in testMethod: I am object " + this.test + "\nClass var set in constractor: " + this.class_var);
		};

		//lets init some variables here, consider it as a class constractor
		this.class_var = initvar;

		//THIS IS VERY IMPORTANT TO KEEP AT THE END
		return this;
	};

})( jQuery );

$(function() {

	//Now lets create objects
	var object1 = $(document.body).testClass("1");
	var object2 = $(document.body).testClass("2");

	//lets call method testMethod
	object1.testMethod("1");
	object2.testMethod("2");

	//lets lets see what we have at the end
	object1.getVars();
	object2.getVars();

});
// ]]></script>

Great, hope you have a clue now how to build OOP plugins. Now there is a small trick. The way jQuery works is when you call methods .each, .bind, .click, .trigger and etc on the callback you will appear out of your current class scope and the scope will be scope of a new element.

To see it in action, let’s modify our testClass.testMethod() example above:

...
this.testMethod = function(status) {
	this.test = status;

	//lets look for some elements on the page	and assign click event
	$("query").bind('click', function(event){

		if(this.anotherMethod) {
			alert('You are in a testClass scope')
		} else {
			alert('Oops..out of testClass scope')
		}

	});

	//lets loop some elements on the page and assign css		
	$("query").each( function(index, value){

		if(this.anotherMethod) {
			alert('You are in a testClass scope')
		} else {
			alert('Oops..out of testClass scope')
		}

		$(value).css('backgroundColor', '#ff0000')

	});

};
...

When you call method ‘object1.testMethod(“1″)’, .each and .bind calls will send you out of the testClass scope. When you work with complex applications you don’t want to do that. I work with object and I want to stay inside this object without doing a $(‘query’) to get back to it. jQuery has a great solution to it – jQuery.proxy.

jQuery.proxy

jQuery.proxy takes a function and returns a new one that will always have a particular scope.
Lest re-write our ‘this.testMethod’ again.

...
this.testMethod = function(status) {
	this.test = status;

	//lets look for some elements on the page	and assign click event
	$("query").bind('click', jQuery.proxy(function(event){

		if(this.anotherMethod) {
			alert('You are in a testClass scope')
		} else {
			alert('Oops..out of testClass scope')
		}

	}, this));

	//lets loop some elements on the page and assign css		
	$("query").each( jQuery.proxy(function(index, value){

		if(this.anotherMethod) {
			alert('You are in a testClass scope')
		} else {
			alert('Oops..out of testClass scope')
		}

		$(value).css('backgroundColor', '#ff0000')

	}, this));	

};
...

Woolia! Now we are in the ‘testClass’ scope. You can call any class method ‘this.anotherMethod()’ or class ‘this.class_var’.

Using setTimeout and setInterval within the class scope

The setTimeout() method calls a function or evaluates an expression after a specified number of milliseconds. This method is a method of global window object. Anytime you call it from your class it sends you out of the class scope.

...
this.testMethod = function(status) {
	this.test = status;

	setTimeout(function(){
		alert(this.anotherMethod); //UNDEFINED, out of class scope
	}, 1000);

};
...

Solution? Use a jQuery.proxy!

...
this.testMethod = function(status) {
	this.test = status;

	setTimeout(jQuery.proxy(function(){
		alert(this.anotherMethod) //CLASS SCOPE
	}, this), 1000);

};
...

Alternative method to jQuery.proxy

While jQuery is not the only framework in this world. Here is an alternative solution to jQuery.proxy, you can use it with jQuery, or any other javascript framework.

Let’s first extend a global javascript object ‘Function’ with method ‘Apply’, for convenience. You can give it any name you think is appropriate:

Function.prototype.Apply = function() {
	var obj = this;var args = arguments;
	var F = function() {
		return obj.apply(args[0], (args[1]) ? args[1] : []); //added [1] not to pass main object
  	};
	return F;
};

Now, we can start using it. Let’s re-write our ‘this.testMethod’ and use ‘Apply’ instead of jQuery.proxy:

...
this.testMethod = function(status) {
	this.test = status;

	setTimeout(function(){
		alert(this.anotherMethod); //CLASS SCOPE
	}.Apply(this), 1000);

};
...
By Rz Rasel Posted in jQuery