In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
How to write a better Javascript DOM library, I believe that many inexperienced people do not know what to do, so this paper summarizes the causes of the problem and solutions, through this article I hope you can solve this problem.
Currently, jQuery is the de facto library for manipulating the document object Model (DOM). It can be used in conjunction with the popular client-side MV* framework and has a large number of plug-ins and large communities. As developers' interest in Javascript grows, many people are curious about how native API works and when we should use them directly instead of referencing an extra library.
Recently, I have begun to find more and more problems with jQuery, at least in my use. The vast majority of these involve the core of jQuery and cannot be solved without eliminating backward compatibility-which is very important. Like many people, I continued to use it for a while, browsing all the pesky browser weird patterns every day.
Later, Daniel Buchner created SelectorListener, hence the concept of "live extension (live extensions)". I started thinking about creating a series of functions that would allow us to create non-intrusive DOM components in a better way than anything we've ever used so far. The goal is to review existing API and solutions and create a cleaner, testable, and lightweight library.
Add useful features to the library
It was the idea of live extensions that encouraged me to develop the better-dom project, but there are other interesting features that make it a unique library. Let's take a quick look:
Live extension
Native animation
Built-in microtemplate
Support for internationalization
Live extension
JQuery has a concept called "live event (live events)". With event proxies, it allows developers to deal with existing and future elements. But many situations require more flexibility. For example, if you need to transform DOM in order to initialize a part, the event broker will fall short. Therefore, live extends.
The goal is to define only one extension and make all future elements quickly skip initialization functions, regardless of the complexity of the widget. This is important because it allows us to develop web pages declaratively and perform well in AJAX applications.
The Live extension allows you to manipulate future elements without calling initialization methods.
Let's look at a simple example. Suppose our task is to implement a completely customized prompt box. The hover pseudo-class selector does not help because the location of the prompt box changes as the mouse moves Event proxies are also not appropriate; listening for mouseover and mouseleave events for all elements in the document tree is expensive. The live extension will save you!
DOM.extend ("[title]", {constructor: function () {var tooltip = DOM.create ("span.custom-title"); / / set the title's textContent and hide it initially tooltip.set ("textContent", this.get ("title")) .hide () This / / remove legacy title .set ("title", null) / / store reference for quicker access .data ("tooltip", tooltip) / / register event handlers .on ("mouseenter", this.onMouseEnter, ["clientX", "clientY"]) .on ("mouseleave", this.onMouseLeave) / / insert the title element into DOM .append (tooltip) }, onMouseEnter: function (x, y) {this.data ("tooltip"). Style ({left: X, top: y}). Show ();}, onMouseLeave: function () {this.data ("tooltip"). Hide ();}})
We can define the style of the. custom-title element in CSS:
.custom-title {position: fixed; / * required * / border: 1px solid # faebcc; background: # faf8f0;}
When you insert a new element with a title attribute into the page, the most interesting part happens. The custom prompt box takes effect without calling any initialization methods.
Live extensions are independent; so they do not need to call an initialization method in order for future content to take effect. So they can be used in conjunction with any DOM library to split the UI code into many small separate blocks, thus simplifying the logic of the application.
*, equally important, something about Web components. Part of the specification, the "decorator", is intended to solve similar problems. Currently, it uses a tag-based implementation that binds event listeners to child elements through special syntax. But it's still just an early draft:
"the decorator, unlike the rest of the Web component, does not have a specification."
Native animation
Thanks to Apple, CSS now has good support for animation. In the past, animation was usually implemented using setInterval and setTimeout of Javascript. This used to be a cool feature-but now it looks more like a bad practice. Native animation is always smoother: it is often faster, less expensive, and can be well degraded without browser support.
In better-dom, there are no animate methods: only show, hide, and toggle. The library uses the standards-based aria-hidden attribute to get the state of a hidden element in CSS.
To show how it works, let's add a simple animation to the previously introduced prompt box:
.custom-title {position: fixed; / * required * / border: 1px solid # faebcc; background: # faf8f0; / * animation code * / opacity: 1;-webkit-transition: opacity 0.5s; transition: opacity 0.5s;} .custom-title [aria-hidden=true] {opacity: 0;}
Show () and hide () internally set the value of the aria-hidden attribute to false or true. This allows CSS to handle animation and conversion.
You can see more animations using better-dom in this demo.
Built-in microtemplate
The HTML string is lengthy and tedious. In the process of looking for an alternative, I found a great Emmet. Today, Emmet is a very popular text editor plug-in with beautiful and concise syntax. For example, this HTML:
Body.append ("")
Compare with the corresponding microtemplate:
Body.append ("ul > li.list-item*3")
In better-dom, any method that accepts HTML also accepts Emmet expressions. Abbreviated parsers are fast, so you don't have to worry about paying the performance price. There is also a template precompiled function available if necessary.
International support
Developing a UI component often requires localization-- it's not easy. Over the years, many people have used different methods to solve this problem. In better-dom, I believe that changing the state of a CSS selector is like changing a language.
Conceptually, changing language is the "expression" of changing content. In CSS2, there are several pseudo-class selectors that can be used to describe such a model:: lang and: before. Let's look at the following code:
[data-i18n= "hello"]: before {content: "Hello Maksim!";} [data-i18n= "hello"]: lang (ru): before {content: "
This is a simple trick: the lang attribute of the html element controls the current language, while the value of the content attribute varies according to the current language. By using attributes such as data-i18n, we can maintain textual content in HTML.
[data-i18n]: before {content: attr (data-i18n);} [data-i18n= "Hello Maksim!"]: lang (ru): before {content:
Of course, such CSS is not attractive, so better-com offers two ways to help: i18n and DOM.importStrings. The former is used to update the data-i18n property to the appropriate value, while the latter localizes the string for a specific language.
Label.i18n ("Hello Maksim!"); / / the label displays "Hello Maksim!" DOM.importStrings ("ru", "Hello Maksim!", "thank you!"); / / now if the page is set to ru language, / / the label will display "thank you very much!" Label.set ("lang", "ru"); / / now the label will display "excuse me" / / despite the web page's language
You can also use parameterized strings. Add the ${param} variable directly to the key string:
Label.i18n ("Hello ${user}!", {user: "Maksim"}); / / the label will display "Hello Maksim!"
Make the native APIs more elegant
Usually we want to follow the standards. But sometimes standards are not user-friendly. DOM is a mess, and in order to make it easy to use, we have to wrap it in a convenient API. Although many improvements have been made to the open source library, there are still some parts that can be done better:
Getter and setter
Event handling
Functional method support
GETTER and SETTER
Native DOM elements have the concepts of attributes and properties, but their behavior is not exactly the same. Suppose we have the following tag in a web page:
Better-dom
To explain why DOM is a mess, let's take a look at this:
Var link = document.getElementById ("foo"); link.href; / / = > "https://github.com/chemerisuk/better-dom" link.getAttribute (" href "); / / = >" / chemerisuk/better-dom "link [" data-test "]; / / = > undefined link.getAttribute (" data-test "); / / = >" test "link.href =" abc "; link.href; / / = >" https://github.com/abc" link.getAttribute ("href") / / = > "abc"
An attribute is equivalent to its corresponding string in HTML, but the element's property of the same name may have some strange behavior, such as the one listed above, to generate a fully qualified URL. These differences can sometimes lead to confusion.
In practical use, it is difficult to imagine a scenario in which such a distinction is useful. In addition, developers must always keep in mind which values (attribute or property) are used, which introduces unnecessary complexity.
In better-dom, things are much clearer. Each element has only intelligent getter and setter.
Var link = DOM.find ("# foo"); link.get ("href"); / / = > "https://github.com/chemerisuk/better-dom" link.set (" href "," abc "); link.get (" href "); / / = >" https://github.com/abc" link.get ("data-attr"); / / = > "test"
First, it does a property lookup and, if it is defined, returns for operation. Otherwise, getter and setter act on the corresponding element attributes (attribute). For Boolean values (checked, selected, these), you can directly use true or false to update the value: changing the property of the element will trigger an update of the corresponding attibute (native behavior).
Improved event handling
Event handling is a very important part of DOM, however, I found a fundamental problem: the behavior of passing event objects into element listeners causes developers concerned about testability to have to forge * parameters (event objects) or create an additional function to pass in event attributes that the event handler only needs.
Var button = document.getElementById ("foo"); button.addEventListener ("click", function (e) {handleButtonClick (e.button);}, false)
It's annoying. However, if we abstract the variable part into a parameter, we can get rid of the extra function:
Var button = DOM.find ("# foo"); button.on ("click", handleButtonClick, ["button"])
By default, event handlers are passed into the ["target", "defaultPrevented"] array, so you don't have to add a parameter to get these attributes.
Button.on ("click", function (target, canceled) {/ / handle button click here})
Deferred binding is also supported (I suggest reading Peter Michaux's review on this topic). It is a more flexible replacement for regular event binding in the W3C standard. It is very useful when you need to enable and close method calls frequently.
Button._handleButtonClick = function () {alert ("click!");}; button.on ("click", "_ handleButtonClick"); button.fire ("click"); / / shows "clicked" message button._handleButtonClick = null; button.fire ("click"); / / shows nothing
*, equally important, better-dom does not provide any calls to legacy or inconsistent API in different browsers, such as click (), focus (), and submit (). The only way to call them is to use the fire method, which performs the default behavior when no listener returns false:
Link.fire ("click"); / / clicks on the link link.on ("click", function () {return false;}); link.fire ("click"); / / triggers the handler above but doesn't do a click
Support for functional methods
ES5 specifies some useful array methods, including map, filter, and some. They allow us to use common collection operations in a standard-compliant manner. So now we have projects like Underscore and Lo-Dash that implement these methods on old browsers.
Each element (or collection) in better-dom has the following built-in methods:
Each (it differs from forEach in that it returns this instead of undefined)
Some
Every
Map
Filter
Reduce [Right]
Var urls, activeLi, linkText; urls = menu.findAll ("a") .map (function (el) {return el.get ("href");}); activeLi = menu.children (). Filter (function (el) {return el.hasClass ("active");}); linkText = menu.children (). Reduce (function (memo, el) {return memo | el.hasClass ("active") & el.find ("a"). Get ()}, false)
Avoid problems with jQuery
Most of the following problems cannot be solved in jQuery without giving up backward compatibility. This is why creating a new library seems like a logical solution.
"Magic" $function
The value of [] operator
The question about return false
Find and findAll
"Magic" $function
Everyone has more or less heard of the magic of the $(dollar) function. A single-character name is not descriptive, so it looks like a built-in language operator. This is why $calls can be seen everywhere in the code of inexperienced developers.
In the implementation behind, $is an extremely complex function. Frequent execution, especially in frequent events such as mousemove and scroll, can lead to poor UI performance.
Although there are many articles suggesting caching jQuery objects, developers are still embedding a lot of $in their code because the syntax of the jQuery library encourages this style of code.
Another problem with the $function is that it can be used to do two completely different things. People already like this syntax, but in general, it is a failed function design:
$("a"); / / = > searches all elements that match "a" selector $(""); / / = > creates an element with jQuery wrapper
Better-dom uses several functions to assume the responsibility of the $function in jQuery: find [all] and DOM.create. Find [All] is used to get the element based on the CSS selector. DOM.create creates a new node tree in memory. Their names clearly indicate their responsibilities.
The value of [] operator
Another reason why the $function is called frequently is the square bracket operator. When a new jQuery object is created, all related nodes are stored in numeric properties. Note, however, that the value of such a numeric attribute contains a native element instance (rather than an jQuery-wrapped object):
Var links = $("a"); links [0] .on ("click", function () {...}); / / throws an error $(links [0]) .on ("click", function () {...}); / / works fine
Because of this feature, every functional method in jQuery or other libraries (such as Underscore) requires the current element to be wrapped with $() in the callback function. Therefore, developers must always keep in mind the type of object they are working on-a native element or a wrapped object-despite the fact that they are using a library that manipulates DOM.
In better-dom, the square bracket operator returns a library object, so developers can forget the native elements. There is only one acceptable way to get native elements: use a special legacy method.
Var foo = DOM.find ("# foo"); foo.legacy (function (node) {/ / use Hammer library to bind a swipe listener Hammer (node) .on ("swipe", function (e) {/ / handle swipe gesture here);})
In fact, there are only very rare situations where this method is required, such as compatibility with a native method, or another DOM library (such as Hammer in the example above).
The question about RETURN FALSE
I've been struggling with the strange interception behavior after returning false in the jQuery event handler. According to W3C standards, it should cancel the default behavior in most cases. In jQuery, return false also blocks event proxies.
Such a capture can cause problems:
Calling stopPropagation () on its own may cause compatibility problems because it prevents the execution of listeners related to other tasks.
2 most developers (even some experienced ones) are not aware of such behavior
It is not clear why the jQuery community decided not to follow the standards. But better-dom won't make the same mistake. So, as everyone expected, the return false in the event handle only prevents the browser from default behavior and does not interfere with event bubbling.
FIND and FINDALL
Element lookup is one of the expensive operations in browsers. Two native methods can be used to implement it: querySelector and querySelectorAll. The difference is that the former stops searching after matching * * results.
This feature allows us to significantly reduce the number of iterations in specific situations. In my test, the speed increased to 20 times! And, predictably, depending on the size of the document tree, the promotion may reach more.
JQuery provides a find method that uses querySelectorAll for general situations. There is currently no function that uses querySelector to get only * matching elements.
The better-dom library has two separate methods: find and findAll. They allow us to use querySelector optimization. To assess potential performance improvements, I searched all the source code for my last business project for the use of these methods:
Find
Match 103 times in 11 files
FindAll
Match 14 times in 4 files
It is clear that the find method is much more popular. This shows that querySelector optimization makes sense in most cases and can drive considerable performance improvements.
Conclusion
Live extensions do make it a lot easier to solve front-end problems. Splitting the UI into many small chunks leads to a more independent and maintainable solution. But as we have shown, a framework is not just about these (although this is the main goal).
One of the things I learned in the development process is that if you don't like a standard, or if you have a different opinion on how to do something, then implement it and prove that your method works.
After reading the above, do you know how to write a better Javascript DOM library? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un