Blog-Archiv

Sonntag, 7. Dezember 2014

jQuery for Beginners

You might have heard that JavaScript is a sometimes quite misleading programming language and contains a number of severe design flaws.

You might have heard that JavaScript applications may be browser-specific, meaning in some browsers exist JS properties or functions that do not exist in others (f.i. window.stop).

To overcome these obstacles, and to standardize JS implementations in web pages, the jQuery library exists. Although it is not a polyfill library, you can be quite sure that every jQuery function you call will work on any browser. Version 1.x even supports InternetExplorer 6, although version 2.x supports IE only since 9.

Basics

jQuery is a well-documented library with good tutorials. It is plain JavaScript (JS), not a JS interpreter, and not a separate language (like CoffeScript, TypeScript, and Dart, which all compile to JS).

Its mission is to search through, and manipulate, the HTML DOM (document object model) of a web page. To be able to support even complex DOM queries, jQuery is a full-featured CSS interpreter that understands even more than CSS 3. The core source file has about 10 000 lines of code (uncompressed).

Here is a jQuery function call example:

$("div").hide();

As the API documentation of hide() tells us, this makes elements in the HTML document invisible. Which elements are affected is estimated by the $("div") expression. The $ sign is the jQuery element selector function. You could also write

jQuery("div").hide();

$ is an allowed JS identifier character and thus can be used as name for any function, variable, or parameter. jQuery uses it as an alias name for its element selector function, which makes jQuery code short and "intuitively" readable.

Mind that the prototype.js library also uses $ as a global function, delegating to document.getElementById() there. To avoid conflicts, the jQuery.noConflict() function exists.

The element selector function accepts a CSS selector string, and optionally an HTML/DOM element context it should use for search. If the context is not given, it searches the entire HTML document. That function then returns a jQuery DOM element wrapper that represents the addressed HTML element(s). On this wrapper you can call functions that you normally can not call on a DOM element, in this example hide().

Passing parameters of other type than string will return a wrapper that represents the parameter as jQuery object, for example you can turn a normal DOM object into a jQuery element wrapper:

var domElement = document.getElementById("MyId");
var jqueryElement = $(domElement);

This is the same as

var jqueryElement = $("#MyId");

Most work can be done using the functions provided by these element wrappers. All functions referred with a leading dot in the API-documentation relate to them. You could regard these functions to be "object-oriented", working on wrapper they are attached to. Such a wrapper sometimes represents more than one HTML/DOM element. When you call a function on it, all wrapped elements will be affected. The number of elements is in the length property of the wrapper:

var allDivs = $("div");
var length = allDivs.length;
console.log("Found "+length+" div elements in document");

Other jQuery functions are so-called utility functions. You could regard them to be "static" (in a Java sense). For example there is a jQuery.each() loop, and jQuery.extend() for making objects inherit from other objects (JS functional inheritance). You could also write them as $.each() and $.extend() (mind that there is also an "object-oriented" .each() function variant).

So you have three kinds of functions with jQuery:

  • the "main" selector function: jQuery(".myclass"), or $(".myclass")
  • element wrapper functions on the selector result: result.hide()
  • utility functions: jQuery.each(map, loopFunction), or $.each(map, loopFunction)

Safe Loops

With $.each() you can safely loop JS maps (= objects) and arrays:

$.each(arrayOrMap, function(indexOrKey, value) {
  alert(indexOrKey+": "+value);
});

This is the preferable way to write loops when including jQuery, it will prevent the JS for-in loop gotcha.

Here is an example for the "object-oriented" variant of each():

$("li").each(function(index) {
  console.log(index+": "+$(this).text());
});

This loops over li elements via a function representing the loop body. The current loop element is addressed using $(this) when wanting it as jQuery wrapper, else simply as this. As you always can omit function parameters in JS, the index is optional.

Queries

The jQuery element selector function supports CSS 3 and some other things specified in its selector API documentation. So be aware that jQuery selectors could be more than CSS 3 can process. Mind also that there are quite complex rules for escaping characters.

Here is a HTML skeleton for jQuery tests:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
  <head>
    <title>jQuery Tests</title>
    <!-- import the latest jQuery from www -->
    <script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
  </head>
     
  <body>
    <div id="log"></div>

    <script type="text/javascript">
      var log = $("#log");
      log.text("variable $ is "+$);
    </script>

  </body>

</html>

This imports the latest jQuery library from the internet (you need to be "online" for that). It then logs the global jQuery function into the div with id "log":

variable $ is function (a,b){return new m.fn.init(a,b)}

Here are some examples of what the jQuery CSS interpreter can process:

jQuery callselects all elements ...
$("*")... in document
$(".myclass")... with a class-attribute of value "myclass"
$("#myid")... with an id-attribute of value "myid"
$(".myclass #myid") ... with an id-attribute of "myid" somewhere below an element with a class-attribute of "myclass"
$(".myclass > .yourclass") ... with an id-attribute of "yourclass" directly below an element with a class-attribute of "myclass".
Use also other CSS combinators +, ~
$("a[href $= '.pdf'], a[href $= '.xml']") ... of type a with an href-attribute value that ends with ".pdf" or ".xml"
$("a[href ^= 'styles'][href $= '.css']") ... of type a with an href that starts with "styles" and ends with "css".
Mind that there MUST NOT be space between the "][" boxes!
$("a:not([href *= 'css'], [href *= 'js'])") ... of type a without an href, or with an href that contains neither "css" nor "js"
$("a:not([href ^= 'styles'][href $= '.css'])") ... of type a either without an href, or not having an href that both starts with "styles" and ends with "css"
$("div:contains('John')") ... of type div that contain the text "John", or one of its children (of any depth) contains it

As one can see, the AND / OR logic is interfered by the unclear CSS way to define such: in CSS, a logical AND is possible only with classes. Read more about supported selectors on its API documentation.

As said above, the result of a "jquery-query" (shouldn't it have been named jsQuery?) is a wrapper on which you can call further jQuery functions. Some of these let apply further filters, which gives much more elegant code than the cryptic CSS selectors above. For example, a logical NOT can be easily applied:

$("li").not(":even")

This would give all li elements with an odd index.

The most important of the other traversing functions are

Generally these functions are the preferable way to navigate the DOM tree, because CSS selectors often lack readability.

Manipulation

After having found the elements to modify, many jQuery functions are present to do this. But keep in mind that there could be more than one element in the selector function's result. All of them will be affected by what you call.

Some of the manipulation functions follow the pattern

var value = element.attr("id");
element.attr("id", "newValue");

That is, for shortness no getXXX() and setXXX() exist, but just an xxx() without or with parameter (= getter / setter).

Here is an example that first queries and then sets a CSS property on an element:

var cssDisplay = element.css("display");
if (cssDisplay === "none")
  element.css("display", "");

What would happen when there are several elements in the wrapper and you call a getter?
This differs from case to case. The css() function returns the value from just the first element. The same does attr(), val(), and most others. But text() for example returns the combined text from all sub-nodes.

Here is an outline of the most important element modification functions:

The most important DOM modification functions:

Resume

jQuery is open source, but don't expect that you can find and/or understand the source code of some function you would like to know. For example, try to find the get() implementation in current jQuery source code.

The jQuery function names are a little bit too short. I had gotchas several times because I wrote attribute("type") instead of attr("type"). They forgot about the good old Java long names convention.

For what do I need several different functions to insert a node? Why do we need to understand all these variants that do the same? When I look for an insert function, I need to study all of them. We do not want to do it in different ways.

Code written using jQuery is breathtaking short and concise. The fact that one function call possibly works on several result elements may be unfamiliar for Java programmers.

Not everything works immediately like intended. But it evolves faster with jQuery than with plain JavaScript.



Keine Kommentare: