Awesome hands-on. Create a detailed tutorial on JavaScript framework _javascript Tips

Source: Internet
Author: User
Tags processing text tagname

Think MooTools incredible? Want to know how dojo is implemented? Curious about jquery's skills? In this tutorial, we'll look at the secrets behind the framework and try to build a simple version of your favorite framework.

We use a variety of javascript frameworks almost every day. When you get started, the handy DOM (Document Object model) operation makes you think that something like jquery is great. This is because: first of all, Dom is too difficult for beginners to understand; Of course, it's not good for an API to be hard to understand. Second, the compatibility problem between browsers is very disturbing.

    • We wrap the elements into objects because we want to be able to add methods to the object.
    • In this tutorial, we will try to implement one of these frameworks from scratch. Yes, it would be fun, but before you get too excited, I want to clarify a few points:
    • This is not going to be a perfectly functional framework. Yes, we're going to write a lot of things, but it's not jquery. But what we're going to do will let you experience the feeling of actually writing the framework.
    • We do not intend to guarantee a full range of compatibility. The framework we're going to write can work on Internet Explorer 8+, Firefox 5+, Opera 10+, Chrome, and Safari.
    • Our framework does not cover all of the possible features. For example, our append and Preappend methods work only when you pass it to an instance of our framework, and we do not use native DOM nodes and node lists.

Also: Although we won't write test cases for our framework in the tutorial, I've done it the first time I've developed it. You can get the code for the framework and test cases from the GitHub.


First step: Create a framework template

We'll start with some packaging code that will accommodate our entire framework. This is a typical immediate function (Iife).

Window.dome = (function () {
 function Dome (ELS) {
 }
 var dome = {
  Get:function (selector) {
  }
 };< C7/>return Dome;
} ());

As you can see, our framework is called Dome, because it is a basic DOM framework. Yes, basic (lame has "lame", "incomplete" meaning, dom plus lame equals dome).

We've got a few things. First, we have a function; it becomes the constructor of the object instance of the construction framework; those objects will contain the elements we select and create.

Then we have a dome object, which is our frame object, and you can see that it eventually returns to the function caller as the return value of the function (the assignment to Window.dome). There is also an empty get function that we will use to select elements from the page. So let's fill in the code.

Step two: Get the element

The dome get function has only one argument, but it can be a lot of things. If it's a string, we'll assume it's a CSS (cascading style sheet) selector, but we might also get a DOM node or a list of DOM nodes.

Get:function (selector) {
 var els;
 if (typeof selector = = "string") {
  els = Document.queryselectorall (selector);
 } else if (selector.length) {
   els = selector;
 } else {
  els = [selector];
 }
 return new Dome (ELS);
}

We use Document.queryselectorall to simply select elements: Of course, this will limit our browser compatibility, but it is acceptable for this situation. If selector is not a string type, we will check its Length property. If it exists, we know we have a list of nodes; otherwise, it's a separate element, and we put it in an array. This is because we are going to pass an array to dome below. As you can see, we returned a new dome object. Let's go back to the Dome function and populate it with code.

Step three: Create a dome instance

This is the Dome function:

function Dome (ELS) {for
 (var i = 0; i < els.length; i++) {
  this[i] = els[i];
 }
 This.length = els.length;
}

I strongly urge you to delve into some of your favorite frames.

It's very simple: we just iterate through all the elements of the ELS and store them in a new number-indexed object. Then we added a length property.

But what's the point? Why not return elements directly? Because: we wrap the elements into objects because we want to be able to add methods to the objects, and these methods allow us to iterate over these elements. This is actually the condensed version of the jquery solution.

Our dome object has returned, now let's add some methods for its prototype (prototype). I'm going to write those methods directly under the Dome function.

Fourth step: Add a few utilities

The first features to add are simple tool functions. Since the Dome object may contain at least one DOM element, we need to iterate through all the elements in almost every method, so that these tools will be able to give force.

We start with a map function:

Dome.prototype.map = function (callback) {
 var results = [], i = 0;
 for (; i < this.length; i++) {
  Results.push (Callback.call (this, this[i), i);
 }
 return results;
};

Of course, this map function has an incoming parameter, a callback function. We iterate through all the elements of the Dome object, collecting the return value of the callback function to the result set. Notice how we call the callback function:

Callback.call (this, this[i], i);

In this way, the function is invoked in the context of the dome instance, and the function receives two parameters: the current element and the element ordinal.

We also want a foreach function. In fact, it's simple:

Dome.prototype.forEach (callback) {
 This.map (callback);
 return this;
};

Since the difference between the map function and the foreach function is just that the map needs to return something, we can simply pass the callback to This.map and ignore the returned array; instead of returning, we will return this to make our library a chain. foreach is called frequently, so note that when a function's callback is returned, in fact, the dome instance is returned. For example, the following method actually returns the dome instance:

DOME.PROTOTYPE.SOMEMETHOD1 = function (callback) {
 This.foreach (callback);
 return this;
};
DOME.PROTOTYPE.SOMEMETHOD2 = function (callback) {return
 This.foreach (callback);
};

There's another one: Mapone. It's easy to know what this function does, but the real question is, why do you need it? This requires something we call "library philosophy".
a brief "philosophical" interpretation

    • First of all, for a beginner, Dom is a tangled one; its API is imperfect.

If building a library is just writing code, it's not difficult. But when I developed this library, I found that the imperfect parts determined how a certain number of methods were implemented.

Soon, we're going to build a text method that returns the selected elements. If the Dome object contains multiple DOM nodes (such as Dome.get ("Li"), what does it return? If you're like jquery ($ ("Li"). Text () is very simple to write, you will get a string, this string is a direct concatenation of the text of all elements. Does it help? I think it's useless, but I don't think there is a better way.

For this project, I will return the text of multiple elements as an array, unless there is only one element in the array, then I simply return a text string instead of an array containing an element. I think you will often go to get the text of a single element, so we optimized that situation. However, if you want to get the text of multiple elements, our return will also be very cool.
Back to Code

So, the Mapone method simply runs the map function and then returns an array, or an array of elements. If you're still unsure how useful this is, stick to it and you'll see!

Dome.prototype.mapOne = function (callback) {
 var m = This.map (callback);
 return m.length > 1? M:M[0];
};


5th step: Processing text and HTML

Next, let's add the text method. Like jquery, we can pass a string value, set the node element's text value, or get the returned text value through the parameterless method.

Dome.prototype.text = function (text) {
 if (typeof text!== "undefined") {return
  This.foreach (function (EL) {
   
    el.innertext = text;}
  );
 else {return
  this.mapone (function (EL) {return
   el.innertext;
  });
 }
;

   

As you expected, when we set (setting) or get (getting) value, we need to check the value of the text. Note that if the Justif (text) method does not work, it is because the text is an empty string is an incorrect value.

If we set (setting), we use a foreach to traverse the elements and set their innertext properties. If we get (getting), the innertext attribute of the element is returned. The Mapone method is to note that if we are working with more than one element, we will return an array, and the other is a string.

If the HTML method uses the innerHTML property instead of the innertext, it will gracefully handle things that involve text text.

Dome.prototype.html = function (HTML) {
 if (typeof html!== "undefined") {
  This.foreach (function (EL) {
   el.in nerhtml = html;
  });
  return this;
 } else {return
  this.mapone (function (EL) {return
   el.innerhtml;
  });
 }
;

As I said: almost the same.

Sixth step: Modify the Class

Next, we want to operate on the class, so add addclass () and Removeclass (). The addclass () parameter is an array of class names or names. In order to realize the dynamic parameters, we need to judge the type of the parameter. If the argument is an array, iterate through the array, add the elements to the class name, and if the argument is a string, add the class name directly. The function needs to make sure that the original class name is not messed up.

Dome.prototype.addClass = function (classes) {
 var className = "";
 if (typeof classes!== "string") {for
  (var i = 0; i < classes.length; i++) {
   ClassName = "" + classes[i];< c19/>}
 } else {
  className = "" + Classes;
 }
 Return This.foreach (function (EL) {
  el.classname + = ClassName;
 });

Pretty intuitive, huh? Hey

Now, writing down Removeclass () is just as easy. However, only one class name is allowed to be deleted at a time.

Dome.prototype.removeClass = function (clazz) {return
 This.foreach (function (EL) {
  var cs = El.className.split ( ""), I;
  while ((i = Cs.indexof (clazz)) >-1) {
   cs = cs.slice (0, I). Concat (Cs.slice (++i));
  El.classname = Cs.join ("");
 });

For each element, we split the el.classname into an array of strings. Then we use a while loop to connect until the Cs.indexof (clazz) return value is greater than-1. We're going to join the results into el.classname.

Seventh step: Fix a bug caused by IE

The worst browser we've dealt with is IE8. In this small library, only one IE caused bugs need to be repaired; And thankfully, fixing it is very simple. IE8 methods that do not support array indexof; we need to use it in the Removeclass method, let's do it here:

if (typeof Array.prototype.indexOf!== "function") {
 Array.prototype.indexOf = function (item) {for
  (var i = 0; i < this.length; i++) {
   if (this[i] = = Item) {return
    i;
   }
  }
  return-1;}

It looks very simple, and it's not fully implemented (it doesn't support the use of the second parameter), but it can achieve our goal.

Step 8th: Adjust the properties

Now, we want a attr function. This will be easy because it is almost the same as the text method or the HTML method. Like these methods, we are able to set and get properties: We will set the name and value of a property and only get the value through the parameter name.

Dome.prototype.attr = function (attr, Val) {
 if (typeof Val!== "undefined") {return
  This.foreach (function (EL) { C4/>el.setattribute (attr, Val);}
  );
 else {return
  this.mapone (function (EL) {return
   el.getattribute (attr);
  });
 }
;

If a parameter has a value, we will iterate through the element and set the property value through the element's SetAttribute method. In addition, we will use the Mapone return to get the parameters through the GetAttribute method.

Step 9th: Create an element

Like any good frame, we should be able to create elements as well. Of course, there is no good way to do demo, so let's add the method to the demo project.

var dome = {//Get-Method-here
 create:function (tagName, attrs) {
 }
};

As you can see, we need two formal parameters: the element name, and a Parameter object. Most of the properties are used through our ARRT method, but TagName and attrs have special treatment. We use the AddClass method for the ClassName property and use the text method for the Text property. Of course, we first want to create elements, and demo objects. The following are all the roles:

Create:function (TagName, attrs) {
 var el = new Dome ([Document.createelement (TagName)]);
  if (attrs) {
   if (attrs.classname) {
    el.addclass (attrs.classname);
    Delete attrs.classname;
   }
  if (attrs.text) {
   el.text (attrs.text);
   Delete attrs.text;
  }
  for (var key in attrs) {
   if (Attrs.hasownproperty (key)) {
    el.attr (key, Attrs[key]);
 }} Return el;
}

As above, we created the element to send him to the new Dmoe object. Next, we deal with all the attributes. Note: When the classname and text properties are used, we have to delete them. This will ensure that when we traverse the other keys, they can also be used. Of course, we end up by returning to this new demo object.

We created new elements, and we wanted to insert those elements into the DOM, right?

10th Step: Tail Add (appending) and head Add (prepending) Element

Next, we will implement the tail add and head Add method. Taking into account a variety of scenarios, implementing these methods can be tricky. Here are the results we want to achieve:

Dome1.append (dome2);
Dome1.prepend (dome2);

IE8 is a wonderful thing for us.

Tail Add or head additions, including the following scenarios:

    • A single new element is added to a single or multiple existing elements
    • Multiple new elements are added to a single or multiple existing elements
    • A single existing element is added to a single or multiple existing elements
    • Multiple existing elements are added to a single or multiple existing elements

Note: the "new element" here indicates that the node element in the DOM has not yet been added, and "existing element" refers to a node element that already exists in the DOM.
Now let's take it one step at a time:

Dome.prototype.append = function (els) {
 This.foreach (function (Parel, i) {
  Els.foreach (function (Childel) {
  });
 });
};

Assume that the parameter Els is a DOM object. A full-featured Dom library should be able to handle nodes or node sequences (nodelist), but we do not request them now. First iterate through the elements (the parent element) that need to be added, and then iterate through the elements (child elements) that will be added in the loop.
If you add a child element to more than one parent element, you need to clone the child element (to avoid the last action from removing the previous addition). However, there is no need to clone at the time of first addition, just clone in other loops. Therefore the processing is as follows:

if (i > 0) {
 Childel = Childel.clonenode (True);
}

The variable i comes from the outer foreach Loop: It represents the serial number of the parent element. The first parent element adds the child element itself, while the other parent element adds a clone of the target child element. Because the child elements passed in as parameters are not cloned, all nodes are responsive when a single child element is added to a single parent element.
Finally, the real add element operation:

Parel.appendchild (Childel);

So, together, we get the following implementations:

Dome.prototype.append = function (els) {return
 This.foreach (function (Parel, i) {
  Els.foreach (function ( Childel) {
   if (i > 0) {
    Childel = Childel.clonenode (True);
   }
   Parel.appendchild (Childel);
  });};

Prepend method

We follow the same logic to achieve the Prepend method, in fact, is quite simple.

Dome.prototype.prepend = function (els) {return
 This.foreach (function (Parel, i) {for
  (var j = els.length-1; j >-1; j--) {
   Childel = (i > 0)? Els[j].clonenode (True): Els[j];
   Parel.insertbefore (Childel, Parel.firstchild);}}
 );

The difference is that when you add multiple elements, the order in which they are added is reversed. So instead of using a Foreach loop, you can replace it with an in-reverse for loop. Similarly, target child elements need to be cloned when they are added to a non-first parent element.

11th Step: Delete Node

For the operation of our last node, removing these nodes from the DOM is simple and requires only:

 
Dome.prototype.remove = function () {return
 This.foreach (function (EL) {return
  el.parentNode.removeChild ( EL);
 };

You only need to delete the child node method through the iteration of the node and the call to the parent node. The good thing is that this Dom object still works (thanks to the Document Object model). We can use the method that we want to use on it, including inserting, interpolating back to DOM, it's pretty, isn't it?

Step 12th: Event handling

Finally, it's the most important part, and we're going to write a few event-handling functions.

As you know, IE8 still uses the old IE event, so we need to detect this. At the same time, we should be prepared to use the DOM level 0 event.

Look at the following methods, which we'll discuss later:

Dome.prototype.on = (function () {
 if (document.addeventlistener) {return
  function (EVT, FN) {return
   this.f Oreach (function (EL) {
    el.addeventlistener (evt, FN, false);});}
  ,
 else if (document.attachevent) { C8/>return function (evt, FN) {return
   This.foreach (function (EL) {
    el.attachevent (' on ' + evt, fn);
   });
  };
 } else {return
  function (EVT, FN) {return
   This.foreach (function (EL) {
    el[' on ' + evt] = fn;
   });
  };
 }
} ());

Here, we use the immediate execution function (Iife), in which we do feature detection. If the Document.addeventlistener method exists, we use it, and we also detect document.attachevent, and if not, use the DOM level 0 method. Notice how we return the final function from the immediate execution function: it will eventually be assigned to Dome.prototype.on. When performing feature detection, it is more convenient to allocate the appropriate method in this way than when testing each run function.

The event-binding method off is similar to the on method:.

Dome.prototype.off = (function () {
 if (document.removeeventlistener) {return
  function (EVT, FN) {return
   th Is.foreach (function (EL) {
    el.removeeventlistener (evt, FN, false);};}
  ;
 } else if ( Document.detachevent) {return
  function (EVT, FN) {return
   This.foreach (function (EL) {
    el.detachevent (" On "+ evt, fn);}
   );
  }
 else {return
  function (EVT, FN) {return
   This.foreach (function (EL) {
    el[' on ' + evt] = null;
   });
  };
 }
} ());

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.