It has 7,384 words and takes 10 minutes to read. This article is the second in a series on cracking the front-end interview. It looks at DOM, events, browser-side optimization, data structures, and algorithms. Some of you might ask DOM what to talk about, except for the various operations on nodes. DOM is the cornerstone of web building. Only by mastering various operations, knowing possible problems, and being familiar with optimization methods, can you take your time in engineering practice. Links to a series of articles: Closures. Let’s talk about DOM.

How to modify the page content?

When evaluating candidates’ knowledge of DOM basics, I often ask questions like:

    It is also agreed that

      nodes can be added with id or class attributes as required to facilitate obtaining node references.

    More than 80% of the candidates can complete the requirements, add the selector for UL first:

    <ul id="list"></ul>Copy the code

    Then give the node creation code:

    var container = document.getElementById('list');
    for (var i = 0; i < 3; i++) {
        var item = document.createElement('li');
        item.innerText = i + 1;
        container.appendChild(item);
    }Copy the code

    There are also candidates who give the following code:

    var container = document.getElementById('list');
    var html = [];
    for (var i = 0; i < 3; i++) {
        html.push('<li>' + (i + 1) + '</li>');
    }
    container.innerHTML = html.join('');Copy the code

    If you can’t write it, you’ll have to face the wall. (Maybe you can write it with all kinds of libraries and frameworks, but when you need to debug bugs and analyze problems, you won’t have enough money.) You may also be wondering, is it code first or interview first? It can be said that code is the most important output of an engineer, and watching a candidate code can familiarize you with his way of thinking, coding style, and coding habits. It is easy to tell if a candidate is “right” or not.

    Frankly, the above two pieces of code can only be said to meet the requirements, but bonus points will be given if they do:

    1. Variable name: variable of the node class, plusndPrefix will be easier to identify, of course, there are students used to borrowjqueryIn the$For more on variable naming, read The Art of Readable Code.
    2. Selector naming: separate CSS selectors from JS selectors, and recommend adding to JS selectorsjs-J-Prefixes, improved readability, and if there are any other benefits, please consider;
    3. Error tolerance: The existence of nodes should be checked so that the code is more robust. In practice, it is very likely that your code will screw up other functions, because a single JS error may cause subsequent code not to execute. Why do this? For those of you who don’t understand, look at defensive programming;
    4. The principle of minimum scope: Code segments should be wrapped in declaration-as-executed function expressions (IIFE), without global variables and without the risk of variable name conflicts, which are important to keep in mind when maintaining legacy code.

    Here is an improved version (for the first code only) that combines the above four points:

    (() => { var ndContainer = document.getElementById('js-list'); if (! ndContainer) { return; } for (var i = 0; i < 3; i++) { var ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndContainer.appendChild(ndItem); }}) ();Copy the code

    After the candidate gives the code, I often ask: is there another way to select nodes? What else? Leave that question to yourself.

    Question 1: How to bind events?

    Now that you have content on the page, add the interaction. Question: What should I do about the contents of the alert when each

  • is clicked? Some candidates gave the following code off the top of their head:
  • / /... for (var i = 0; i < 3; i++) { var ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndItem.addEventListener('click', function () { alert(i); }); ndContainer.appendChild(ndItem); } / /...Copy the code

    Or the following code:

    / /... for (var i = 0; i < 3; i++) { var ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndItem.addEventListener('click', function () { alert(ndItem.innerText); }); ndContainer.appendChild(ndItem); } / /...Copy the code

    If you have a good understanding of closures and scopes, it’s easy to spot the problem: alert is always 3, not every

  • text. Neither code meets the requirement because the scope of I and ndItem is the same. Using ES6’s block-level scope solves this problem:
  • / /... for (let i = 0; i < 3; i++) { const ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndItem.addEventListener('click', function () { alert(i); }); ndContainer.appendChild(ndItem); } / /...Copy the code

    Candidates familiar with the addEventListener document will give the following methods:

    / /... for (var i = 0; i < 3; i++) { var ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndItem.addEventListener('click', function () { alert(this.innerText); }); ndContainer.appendChild(ndItem); } / /...Copy the code

    Since the default “this” in EventListener refers to the current node, those who prefer using arrow functions should be careful because they force the execution context of the function to change. I judge the standard is to pass here, did you pass?

    At this point, I sometimes ask: Is there any other way to bind events besides addEventListener? What are the problems with using onclick?

    Follow-up 2: After the data quantity increases?

    None of the above seems to be challenging, but don’t worry, the difficulty continues to increase. What if I want to insert 300

  • ?
  • Some students will roughly change the loop termination condition to I < 300, which is not an obvious problem, but if you think about it, the number of event listeners registered in the DOM has increased by 100 times. Is there a better way? As you read this, Event Delegation, yes.

    Using event delegate can effectively reduce the number of event registrations, and dynamically increase or decrease the number of child nodes without modifying the code. The code using event delegate is as follows:

    (() => { var ndContainer = document.getElementById('js-list'); if (! ndContainer) { return; } for (let i = 0; i < 300; i++) { const ndItem = document.createElement('li'); ndItem.innerText = i + 1; ndContainer.appendChild(ndItem); } ndContainer.addEventListener('click', function (e) { const target = e.target; if (target.tagName === 'LI') { alert(target.innerHTML); }}); }) ();Copy the code

    If you don’t know what event delegation is, implementation principle is what, what are the advantages of using it, please take the time to study, can help you write better code, haven’t heard event delegate candidate I will ask “standard DOM events process”, if you are familiar with, and led him to understand event delegation, until you write code, This process can tell if a candidate is thinking flexibly.

    Getting back to the point, quite a bit of code is prone to all sorts of problems as it gets bigger. What would be the problem if 30,000

  • were to be inserted into
      ? How does the code need to be improved? Almost certainly, the page experience will not flow smoothly, and even will be a noticeable sense of lag, how to solve the problem?
  • The main reason for the stutter is that the DOM structure is modified with each loop, plus the large loop takes too long to execute and the browser’s rendering frame rate (FPS) is too low. In fact, with a long list of 30,000

  • , the user will not see all of them immediately, or most of them will not see them at all, and there is no need to render them. Fortunately, modern browsers provide the requestAnimationFrame API to solve the problem of time-consuming code blocks to render. If you don’t know how requestAnimationFrame works, please read this article. RequestAnimationFrame works in React and Angular. If you understand how requestAnimationFrame works, It’s easy to understand the latest React Fiber algorithm.
  • Based on the above analysis, the blocking time of the main thread can be reduced from two aspects: reducing DOM operation times and shortening loop time. The best way to reduce the number of DOM operations is DocumentFragment. To shorten the loop time, consider using divide-and-conquer to insert 30,000

  • batches into the page before the page is re-rendered. Since requestAnimationFrame is not supported by all browsers, Paul Irish provides a polyfill for requestAnimationFrame, and this Gist is well worth learning.
  • Here is a complete code example:

    (() => { const ndContainer = document.getElementById('js-list'); if (! ndContainer) { return; } const total = 30000; const batchSize = 4; Const batchCount = total/batchSize; // let batchDone = 0; / / completed batch number function appendItems () {const fragments = document. CreateDocumentFragment (); for (let i = 0; i < batchSize; i++) { const ndItem = document.createElement('li'); ndItem.innerText = (batchDone * batchSize) + i + 1; fragment.appendChild(ndItem); DOM ndContainer. AppendChild (fragment); batchDone += 1; doBatchAppend(); } function doBatchAppend() { if (batchDone < batchCount) { window.requestAnimationFrame(appendItems); } } // kickoff doBatchAppend(); ndContainer.addEventListener('click', function (e) { const target = e.target; if (target.tagName === 'LI') { alert(target.innerHTML); }}); }) ();Copy the code

    Those of you reading this have already understood the main points of this section: the impact of mass DOM manipulation on page rendering and optimization methods, and performance is an integral part of the functionality for users.

    Question 3: DOM tree traversal?

    Data structure and algorithm are useless in the eyes of many front-end students, and in fact they are not good at it. However, a solid CS foundation is an essential knowledge reserve for engineers, no matter whether it is front-end or back-end. Only with this reserve can the value of engineers be demonstrated when facing complex problems. Javascript DOM can be naturally associated with the tree data structure, believe everyone is familiar, such as given the following HTML fragment:

    <div class="root">
        <div class="container">
            <section class="sidebar">
                <ul class="menu"></ul>
            </section>
            <section class="main">
                <article class="post"></article>
                <p class="copyright"></p>
            </section>
        </div>
    </div>Copy the code

    For this DOM tree, the code implementation of breadth-first traversal (BFS) is expected to be given. When traversing each node, the type and class name of the current node are printed. For example, the breadth-first traversal result of the tree above is:

    DIV .root
    DIV .container
    SECTION .sidebar
    SECTION .main
    UL .menu
    ARTICLE .post
    P .copyrightCopy the code

    This requires a clear representation of node relationships in the DOM tree. The key attributes are childNodes and Children, which are slightly different. Depth-first traversal (DFS) is easy to write using recursion, but breadth-first requires a data structure like a queue to manage the nodes to be traversed. At this point, grab a pen and paper, think about it for a minute, and see if you can write it yourself.

    The following is a reference implementation, the code is relatively simple, not much explanation:

    const traverse = (ndRoot) => {
        const queue = [ndRoot];

    while (queue.length) {

    const node = queue.shift();

    printInfo(node); if (! node.children.length) { continue; } Array.from(node.children).forEach(x =>queue.push(x));

    }}; const printInfo = (node) => { console.log(node.tagName, `.${node.className}`); }; // kickoff traverse(document.querySelector('.root'));Copy the code

    If you are confused about trees and tree traversal, please read the outer chain above carefully. Finally, ask another question, if you want to print nodes when the node in the tree, how to solve the problem?

    Summarize and think about questions

    This article starts with basic DOM manipulation, then moves on to event binding, rendering performance optimization, and finally data structures and algorithms that engineers can’t avoid. If you were the interviewer, how would you talk to the candidate? If you want to learn the DOM well, this article is not enough. There are 3 questions to think about and more than 10 learning materials to learn.

    One More Thing

    The author of this article is Wang Shijun. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source. If you found this article helpful, please give it a thumbs up! If you have any questions about the content of this article, please leave a comment. Want to know what I’ll write next? Please subscribe to my Nuggets column.