Write good JS principles

Do their job

  • HTML is responsible for structure
  • CSS is responsible for presentation
  • JavaScript is responsible for behavior

Here’s an example:

We want to change the page by clicking on the sun or moon

Solution a:

We can capture the icon click event, click to change the font color and background color.

But this couples JS to CSS

Scheme 2:

We changed js to modify the style directly, and changed js to modify the element class, the style transformation is left to CSS.

Solution 3:

A pure CSS solution without JS

The state is captured in the form of an input check box and a label

When we click on label, the corresponding check box is also clicked

The mode can be switched through the selected state pseudo-class of the checkbox

Either the second or the third option is better than the first option.

Conclusion:

  • HTML/CSS/JS is responsible
  • Unnecessary direct manipulation of styles by JS should be avoided
  • You can use class to represent state
  • Pure presentation class interaction seeks zero JS scheme

Component packaging

A component is a unit of a Web page that contains templates (HTML), functions (JS), and styles (CSS).

Component design principles:

  • encapsulation
  • correctness
  • scalability
  • Reusability.

When encapsulating components, don’t just try to get them right.

We can start with simple features and iterate and optimize.

Component encapsulation can start with the following steps:

Component design steps

  • The structure design

  • Show the effect

  • Behavior design

    • API (Features)
    • Event (Control flow)

The following illustrates the component encapsulation process using an example of creating a multicast diagram component.

The structure design

Structure design is the first step, we need to display all the elements in the HTML document

For a caroute diagram component, it is a container that contains a list of images

<! DOCTYPEhtml>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
    <link rel="stylesheet" href="slider.css">
</head>

<body>
    <div id="my-slider" class="slider-list">
        <ul>
            <li class="slider-list__item--selected">
                <img src="https://img12.360buyimg.com/pop/s590x470_jfs/t1/200666/3/2407/80779/611cbdbfE67561765/802cf07557ad00c6.jpg.webp" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://imgcps.jd.com/ling4/100009077475/5Lqs6YCJ5aW96LSn/5L2g5YC85b6X5oul5pyJ/p-5f3a47329785549f6bc7a6e0/0f1863cd/cr/s /q.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://img13.360buyimg.com/pop/s590x470_jfs/t1/204538/27/418/100695/6110eb81E40c33891/98a22a2cf9021e4e.jpg.webp" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://img10.360buyimg.com/pop/s590x470_jfs/t1/184419/8/18902/98852/6114bd1bEa22d6dbb/1cfa09c57dbf3817.jpg.webp" alt="">
            </li>
        </ul>
    </div>
</body>
<script src="slider.js"></script>

</html>
Copy the code

Display effect

Once the structure is designed, what we see in the browser is an array of elements.

We need to adjust the style of different elements according to the design requirements. Including location, size, color and so on

For the rotation image, we want to set the image to absolute position, opacity of no display to 0, opacity of display to 1

#my-slider {
    position: relative;
    width: 590px;
}

.slider-list ul {
    list-style-type: none;
    position: relative;
    padding: 0;
    margin: 0;
}

.slider-list__item..slider-list__item--selected {
    position: absolute;
    transition: opacity 1s;
    opacity: 0;
    text-align: center;
}

.slider-list__item--selected {
    transition: opacity 1s;
    opacity: 1;
}
Copy the code

After adding CSS, all of the images are centralized and have a rotating image feel. Here is how to control which images are displayed

Behavior design

When designing the behavior of a component, we need to tailor it to its functionality.

Provides apis for various functions during encapsulation.

API (Features)

API design should ensure atomic operation, single responsibility and flexibility.

For a multicast image, we need to be able to control the jump to a given image, to the previous image, to the next image

I did some packaging design

class Slider {
    constructor(id) {
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll(".slider-list__item,.slider-list__item--selected");
    }
    getSelectedItem() {
        const select = this.container.querySelector(".slider-list__item--selected");
        return select;
    }
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }
    sliderTo(index) {
        const select = this.getSelectedItem();
        if (select) {
            select.className = "slider-list__item";
        }
        const item = this.items[index];
        if (item) {
            this.items[index].className = "slider-list__item--selected"; }}sliderNext() {
        const index = this.getSelectedItemIndex();
        const nextIndex = (index + 1) % this.items.length;
        this.sliderTo(nextIndex);
    }
    sliderPrevious() {
        const index = this.getSelectedItemIndex();
        const previousIndex = (index + this.items.length - 1) % this.items.length;
        this.sliderTo(previousIndex); }}Copy the code

Event (Control flow)

Do the previous part we have the control object for the wheel cast diagram

In order to really achieve the effect of rotation graphics, we need to design the control flow

We mainly design three control functions for the rote graph

  1. Page regularly
  2. Click left and right to turn the page
  3. Turn the page at the lower control point

With this control idea in mind, write the elements used for the control into an HTML document

<a class="slider-list__previous">&lt;</a>
<a class="slider-list__next">&gt;</a>
<div class="slider-list__control">
    <span class="slider-list__control-buttons--selected"></span>
    <span class="slider-list__control-buttons"></span>
    <span class="slider-list__control-buttons"></span>
    <span class="slider-list__control-buttons"></span>
</div>
Copy the code

Setting the style for the element

.slider-list__next..slider-list__previous {
    position: absolute;
    top: 200px;
    line-height: 40px;
    padding: 0 5px 5px 5px;
    background-color: black;
    opacity: 0;
    font-size: 30px;
    color: white;
}

.slider-list__next:hover..slider-list__previous:hover {
    opacity: 0.7;
    transition: opacity 0.5 s;
}

.slider-list__next {
    right: 0;
}

.slider-list__control {
    position: absolute;
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
    align-items: center;
    bottom: 40px;
    left: 260px;
    height: 20px;
    width: 70px;
    background-color: rgba(185.183.183.0.911);
    border-radius: 10px;
}

.slider-list__control-buttons..slider-list__control-buttons--selected {
    height: 10px;
    width: 10px;
    background-color: white;
    border-radius: 5px;
}

.slider-list__control-buttons--selected {
    background-color: red;
}
Copy the code

With that in mind, here’s how to use JS to drive the transformation of the element class

For periodic page turning, in fact, is to set a timer, the method of periodic page turning. But since left and right page turns or control point page turns may be used in the control process, we can’t have them in conflict with each other. We need to set the start and stop for the timed page turning. When we turn left and right or turn control points, we need to stop first, and then start after turning over.

We added two methods to the Slider class

start() {
    this.stop();
    this._timer = setInterval(() = > {
        this.sliderNext();
    }, this.cycle)
}
stop() {
    clearInterval(this._timer);
}
Copy the code

For the left and right page flipping, we directly add the click listening event when the Slider class is initialized. After clicking, the timer will be turned off first, and the timer will be turned on again after the page flipping.

/ / forward
const previous = this.container.querySelector(".slider-list__previous");
if (previous) {
    previous.addEventListener('click'.evt= > {
        this.stop();
        this.sliderPrevious();
        this.start(); evt.preventDefault(); })}/ / turn back
const next = this.container.querySelector(".slider-list__next");
if (next) {
    next.addEventListener('click'.evt= > {
        this.stop();
        this.sliderNext();
        this.start(); evt.preventDefault(); })}Copy the code

For control point page-turning, our control point needs to correspond with the current picture displayed at all times, which involves the coupling between the current control point and the current picture. For decoupling, we design a custom event ourselves.

An event is raised when the page turn method is executed.

const detail = { index: index }
const event = new CustomEvent("slide", { bubbles: true, detail })
this.container.dispatchEvent(event);
Copy the code

Then set the monitoring of the event. When the page-turning event is monitored, we modify the current control point.

this.container.addEventListener('slide'.evt= > {
    const idx = evt.detail.index;
    const selected = controller.querySelector('.slider-list__control-buttons--selected');
    if (selected) {
        selected.className = "slider-list__control-buttons";
    }
    buttons[idx].className = "slider-list__control-buttons--selected";
})
Copy the code

Once you’ve done this, you’ve basically wrapped the entire component

The full code is posted here

HTML part

<! DOCTYPEhtml>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
    <link rel="stylesheet" href="slider.css">
</head>

<body>
    <div id="my-slider" class="slider-list">
        <ul>
            <li class="slider-list__item--selected">
                <img src="https://img12.360buyimg.com/pop/s590x470_jfs/t1/200666/3/2407/80779/611cbdbfE67561765/802cf07557ad00c6.jpg.webp" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://imgcps.jd.com/ling4/100009077475/5Lqs6YCJ5aW96LSn/5L2g5YC85b6X5oul5pyJ/p-5f3a47329785549f6bc7a6e0/0f1863cd/cr/s /q.jpg" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://img13.360buyimg.com/pop/s590x470_jfs/t1/204538/27/418/100695/6110eb81E40c33891/98a22a2cf9021e4e.jpg.webp" alt="">
            </li>
            <li class="slider-list__item">
                <img src="https://img10.360buyimg.com/pop/s590x470_jfs/t1/184419/8/18902/98852/6114bd1bEa22d6dbb/1cfa09c57dbf3817.jpg.webp" alt="">
            </li>
        </ul>
        <a class="slider-list__previous">&lt;</a>
        <a class="slider-list__next">&gt;</a>
        <div class="slider-list__control">
            <span class="slider-list__control-buttons--selected"></span>
            <span class="slider-list__control-buttons"></span>
            <span class="slider-list__control-buttons"></span>
            <span class="slider-list__control-buttons"></span>
        </div>
    </div>
</body>
<script src="slider.js"></script>

</html>
Copy the code

The CSS part

#my-slider {
    position: relative;
    width: 590px;
    height: 474px;
}

.slider-list ul {
    list-style-type: none;
    position: relative;
    padding: 0;
    margin: 0;
}

.slider-list__item..slider-list__item--selected {
    position: absolute;
    transition: opacity 1s;
    opacity: 0;
    text-align: center;
}

.slider-list__item--selected {
    transition: opacity 1s;
    opacity: 1;
}

.slider-list__next..slider-list__previous {
    position: absolute;
    top: 200px;
    line-height: 40px;
    padding: 0 5px 5px 5px;
    background-color: black;
    opacity: 0;
    font-size: 30px;
    color: white;
}

.slider-list__next:hover..slider-list__previous:hover {
    opacity: 0.7;
    transition: opacity 0.5 s;
}

.slider-list__next {
    right: 0;
}

.slider-list__control {
    position: absolute;
    display: flex;
    flex-direction: row;
    justify-content: space-evenly;
    align-items: center;
    bottom: 40px;
    left: 260px;
    height: 20px;
    width: 70px;
    background-color: rgba(185.183.183.0.911);
    border-radius: 10px;
}

.slider-list__control-buttons..slider-list__control-buttons--selected {
    height: 10px;
    width: 10px;
    background-color: white;
    border-radius: 5px;
}

.slider-list__control-buttons--selected {
    background-color: red;
}
Copy the code

Js part

class Slider {
    constructor(id, cycle = 3000) {
        this.cycle = cycle;
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll(".slider-list__item,.slider-list__item--selected");

        / / forward
        const previous = this.container.querySelector(".slider-list__previous");
        if (previous) {
            previous.addEventListener('click'.evt= > {
                this.stop();
                this.sliderPrevious();
                this.start(); evt.preventDefault(); })}/ / turn back
        const next = this.container.querySelector(".slider-list__next");
        if (next) {
            next.addEventListener('click'.evt= > {
                this.stop();
                this.sliderNext();
                this.start(); evt.preventDefault(); })}/ / control points
        const controller = this.container.querySelector(".slider-list__control");
        if (controller) {
            const buttons = controller.querySelectorAll('.slider-list__control-buttons,.slider-list__control-buttons--selected');
            controller.addEventListener("mouseover".evt= > {
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    this.sliderTo(idx);
                    this.stop();
                }
            })
            controller.addEventListener('mouseout'.evt= > {
                this.start();
            })
            this.container.addEventListener('slide'.evt= > {
                const idx = evt.detail.index;
                const selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) {
                    selected.className = "slider-list__control-buttons";
                }
                buttons[idx].className = "slider-list__control-buttons--selected"; }}})getSelectedItem() {
        const select = this.container.querySelector(".slider-list__item--selected");
        return select;
    }
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }
    sliderTo(index) {
        const select = this.getSelectedItem();
        if (select) {
            select.className = "slider-list__item";
        }
        const item = this.items[index];
        if (item) {
            this.items[index].className = "slider-list__item--selected";
        }
        const detail = { index: index }
        const event = new CustomEvent("slide", { bubbles: true, detail })
        this.container.dispatchEvent(event);
    }
    sliderNext() {
        const index = this.getSelectedItemIndex();
        const nextIndex = (index + 1) % this.items.length;
        this.sliderTo(nextIndex);
    }
    sliderPrevious() {
        const index = this.getSelectedItemIndex();
        const previousIndex = (index + this.items.length - 1) % this.items.length;
        this.sliderTo(previousIndex);
    }
    start() {
        this.stop();
        this._timer = setInterval(() = > {
            this.sliderNext();
        }, this.cycle)
    }
    stop() {
        clearInterval(this._timer); }}const slider = new Slider("my-slider".2000);
slider.start();
Copy the code

Refactoring one: plug-in

We’ve done everything we wanted, but we can see that there’s a lot of code in the constructor that initializes the sliders as well as the control elements.

The coupling of the control element with our package is too high to be used in combination with other control buttons.

So we want to decouple

  • Extract control elements into plug-ins
  • Pass between plug-in and componentDependency injectionTo establish a connection

We’ll start by adding dependency injection methods to the Slider class

registerPlugins(. plugins) {
    plugins.forEach(plugin= > plugin(this));
}
Copy the code

Then there are three plug-ins outside the class

function pluginPrevious(slider) {
    / / forward
    const previous = slider.container.querySelector(".slider-list__previous");
    if (previous) {
        previous.addEventListener('click'.evt= >{ slider.stop(); slider.sliderPrevious(); slider.start(); evt.preventDefault(); }}})function pluginNext(slider) {
    / / turn back
    const next = slider.container.querySelector(".slider-list__next");
    if (next) {
        next.addEventListener('click'.evt= >{ slider.stop(); slider.sliderNext(); slider.start(); evt.preventDefault(); }}})function pluginController(slider) {
    / / control points
    const controller = slider.container.querySelector(".slider-list__control");
    if (controller) {
        const buttons = controller.querySelectorAll('.slider-list__control-buttons,.slider-list__control-buttons--selected');
        controller.addEventListener("mouseover".evt= > {
            const idx = Array.from(buttons).indexOf(evt.target);
            if (idx >= 0) {
                slider.sliderTo(idx);
                slider.stop();
            }
        })
        controller.addEventListener('mouseout'.evt= > {
            slider.start();
        })
        slider.container.addEventListener('slide'.evt= > {
            const idx = evt.detail.index;
            const selected = controller.querySelector('.slider-list__control-buttons--selected');
            if (selected) {
                selected.className = "slider-list__control-buttons";
            }
            buttons[idx].className = "slider-list__control-buttons--selected"; }}})Copy the code

The three functions were originally executed inside the constructor of the Slider class, depending on the Slider’s this. We just separate these three functions out and pass the Slider’s this parameter to them using the plugin’s registerPlugins method.

const slider = new Slider("my-slider".2000);
slider.registerPlugins(/*pluginController,*/ pluginPrevious, pluginNext)
slider.start();
Copy the code

This way we can comment out any functionality we don’t want to use, or add a button to control it, and then register the plugin into the slider

Refactoring two: Templating

After the pluginization we just did, we actually decoupled the control program from the Slider class.

If we want to control without using control points, we simply comment out the registration where the plug-in is registered

const slider = new Slider("my-slider".2000);
slider.registerPlugins(/*pluginController,*/ pluginPrevious, pluginNext);
slider.start();
Copy the code

This disables our control points method, but we will find that there will still be control points on the page. And the control point does not change with the change of pictures.

So we need to put the HTML code into JS and let JS render the structure.

That leaves our HTML with just one line of code

<div id="my-slider" class="slider-list"></div>
Copy the code

Our CSS doesn’t have to change

js

class Slider {
    constructor(id, opts = { images: [], cycle: 3000 }) {
        this.options = opts;
        this.container = document.getElementById(id);
        this.container.innerHTML = this.render();
        this.items = this.container.querySelectorAll(".slider-list__item,.slider-list__item--selected");
        this.cycle = opts.cycle || 3000;
        this.sliderTo(0);
    }
    render() {
        const images = this.options.images;
        const content = images.map(image= > `
            <li class="slider-list__item">
                <img src="${image}">
            </li>
        `.trim())
        return `
        <ul>${content.join("")}</ul>
        `
    }
    registerPlugins(. plugins) {
        plugins.forEach(plugin= > {
            const pluginContainer = document.createElement('div');
            pluginContainer.className = "slider-list__plugin";
            pluginContainer.innerHTML = plugin.render(this);
            this.container.appendChild(pluginContainer);
            plugin.action(this)}); }getSelectedItem() {
        const select = this.container.querySelector(".slider-list__item--selected");
        return select;
    }
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }
    sliderTo(index) {
        const select = this.getSelectedItem();
        if (select) {
            select.className = "slider-list__item";
        }
        const item = this.items[index];
        if (item) {
            this.items[index].className = "slider-list__item--selected";
        }
        const detail = { index: index }
        const event = new CustomEvent("slide", { bubbles: true, detail })
        this.container.dispatchEvent(event);
    }
    sliderNext() {
        const index = this.getSelectedItemIndex();
        const nextIndex = (index + 1) % this.items.length;
        this.sliderTo(nextIndex);
    }
    sliderPrevious() {
        const index = this.getSelectedItemIndex();
        const previousIndex = (index + this.items.length - 1) % this.items.length;
        this.sliderTo(previousIndex);
    }
    start() {
        this.stop();
        this._timer = setInterval(() = > {
            this.sliderNext();
        }, this.cycle)
    }
    stop() {
        clearInterval(this._timer); }}const pluginPrevious = {
    render() {
        return `< `;
    },
    action(slider) {
        / / forward
        const previous = slider.container.querySelector(".slider-list__previous");
        if (previous) {
            previous.addEventListener('click'.evt= >{ slider.stop(); slider.sliderPrevious(); slider.start(); evt.preventDefault(); })}}}const pluginNext = {
    render() {
        return `> `;
    },
    action(slider) {
        / / turn back
        const next = slider.container.querySelector(".slider-list__next");
        if (next) {
            next.addEventListener('click'.evt= >{ slider.stop(); slider.sliderNext(); slider.start(); evt.preventDefault(); })}}}const pluginController = {
    render(slider) {
        const controller = slider.options.images.map((image, index) = > {
            if (slider.getSelectedItemIndex() == index) {
                return `  `.trim();
            } else {
                return `  `.trim(); }})return `<div class="slider-list__control">${controller.join("")}</div>`;
    },
    action(slider) {
        / / control points
        const controller = slider.container.querySelector(".slider-list__control");
        if (controller) {
            const buttons = controller.querySelectorAll('.slider-list__control-buttons,.slider-list__control-buttons--selected');
            controller.addEventListener("mouseover".evt= > {
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    slider.sliderTo(idx);
                    slider.stop();
                }
            })
            controller.addEventListener('mouseout'.evt= > {
                slider.start();
            })
            slider.container.addEventListener('slide'.evt= > {
                const idx = evt.detail.index;
                const selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) {
                    selected.className = "slider-list__control-buttons";
                }
                buttons[idx].className = "slider-list__control-buttons--selected"; })}}}const images = [
    "https://img12.360buyimg.com/pop/s590x470_jfs/t1/200666/3/2407/80779/611cbdbfE67561765/802cf07557ad00c6.jpg.webp"."https://imgcps.jd.com/ling4/100009077475/5Lqs6YCJ5aW96LSn/5L2g5YC85b6X5oul5pyJ/p-5f3a47329785549f6bc7a6e0/0f1863cd/cr/s /q.jpg"."https://img13.360buyimg.com/pop/s590x470_jfs/t1/204538/27/418/100695/6110eb81E40c33891/98a22a2cf9021e4e.jpg.webp"."https://img10.360buyimg.com/pop/s590x470_jfs/t1/184419/8/18902/98852/6114bd1bEa22d6dbb/1cfa09c57dbf3817.jpg.webp"
]
const slider = new Slider("my-slider", { images, cycle: 2000 });
slider.registerPlugins(pluginController, pluginPrevious, pluginNext)
slider.start();
Copy the code

Refactoring three: Component frameworks

So far, from the point of view of the rotator, this has worked out pretty well

But if we are definitely designing a site with more than one component, we can abstract out a more general component model. Use this component model to create more components.

So we can write an abstract component class

class Component {
    constructor(id, opts = { name, data: [] }) {
        this.container = document.getElementById(id);
        this.options = opts;
        this.container.innerHTML = this.render(opts.data);
    }
    registerPlugins(. plugins) {
        plugins.forEach(plugin= > {
            const pluginContainer = document.createElement('div');
            pluginContainer.className = `The ${this.name}__plugin`;
            pluginContainer.innerHTML = plugin.render(this);
            this.container.appendChild(pluginContainer);
            plugin.action(this)}); }render(component) {
        / / abstract
        return ""; }}Copy the code

Use this abstract component class to design the Slider class

class Slider extends Component {
    constructor(id, opts = { name: "slider-list", data: [], cycle: 3000 }) {
        super(id, opts);
        this.items = this.container.querySelectorAll(".slider-list__item,.slider-list__item--selected");
        this.cycle = opts.cycle || 3000;
        this.sliderTo(0);
    }
    render(data) {
        const content = data.map(image= > `
            <li class="slider-list__item">
                <img src="${image}">
            </li>
        `.trim())
        return `
        <ul>${content.join("")}</ul>
        `
    }
    getSelectedItem() {
        const select = this.container.querySelector(".slider-list__item--selected");
        return select;
    }
    getSelectedItemIndex() {
        return Array.from(this.items).indexOf(this.getSelectedItem());
    }
    sliderTo(index) {
        const select = this.getSelectedItem();
        if (select) {
            select.className = "slider-list__item";
        }
        const item = this.items[index];
        if (item) {
            this.items[index].className = "slider-list__item--selected";
        }
        const detail = { index: index }
        const event = new CustomEvent("slide", { bubbles: true, detail })
        this.container.dispatchEvent(event);
    }
    sliderNext() {
        const index = this.getSelectedItemIndex();
        const nextIndex = (index + 1) % this.items.length;
        this.sliderTo(nextIndex);
    }
    sliderPrevious() {
        const index = this.getSelectedItemIndex();
        const previousIndex = (index + this.items.length - 1) % this.items.length;
        this.sliderTo(previousIndex);
    }
    start() {
        this.stop();
        this._timer = setInterval(() = > {
            this.sliderNext();
        }, this.cycle)
    }
    stop() {
        clearInterval(this._timer); }}const pluginPrevious = {
    render() {
        return `< `;
    },
    action(slider) {
        / / forward
        const previous = slider.container.querySelector(".slider-list__previous");
        if (previous) {
            previous.addEventListener('click'.evt= >{ slider.stop(); slider.sliderPrevious(); slider.start(); evt.preventDefault(); })}}}const pluginNext = {
    render() {
        return `> `;
    },
    action(slider) {
        / / turn back
        const next = slider.container.querySelector(".slider-list__next");
        if (next) {
            next.addEventListener('click'.evt= >{ slider.stop(); slider.sliderNext(); slider.start(); evt.preventDefault(); })}}}const pluginController = {
    render(slider) {
        const controller = slider.options.data.map((image, index) = > {
            if (slider.getSelectedItemIndex() == index) {
                return `  `.trim();
            } else {
                return `  `.trim(); }})return `<div class="slider-list__control">${controller.join("")}</div>`;
    },
    action(slider) {

        / / control points
        const controller = slider.container.querySelector(".slider-list__control");
        if (controller) {
            const buttons = controller.querySelectorAll('.slider-list__control-buttons,.slider-list__control-buttons--selected');
            controller.addEventListener("mouseover".evt= > {
                const idx = Array.from(buttons).indexOf(evt.target);
                if (idx >= 0) {
                    slider.sliderTo(idx);
                    slider.stop();
                }
            })
            controller.addEventListener('mouseout'.evt= > {
                slider.start();
            })
            slider.container.addEventListener('slide'.evt= > {
                const idx = evt.detail.index;
                const selected = controller.querySelector('.slider-list__control-buttons--selected');
                if (selected) {
                    selected.className = "slider-list__control-buttons";
                }
                buttons[idx].className = "slider-list__control-buttons--selected"; })}}}const images = [
    "https://img12.360buyimg.com/pop/s590x470_jfs/t1/200666/3/2407/80779/611cbdbfE67561765/802cf07557ad00c6.jpg.webp"."https://imgcps.jd.com/ling4/100009077475/5Lqs6YCJ5aW96LSn/5L2g5YC85b6X5oul5pyJ/p-5f3a47329785549f6bc7a6e0/0f1863cd/cr/s /q.jpg"."https://img13.360buyimg.com/pop/s590x470_jfs/t1/204538/27/418/100695/6110eb81E40c33891/98a22a2cf9021e4e.jpg.webp"."https://img10.360buyimg.com/pop/s590x470_jfs/t1/184419/8/18902/98852/6114bd1bEa22d6dbb/1cfa09c57dbf3817.jpg.webp"
]
const slider = new Slider("my-slider", { data: images, cycle: 2000 });
slider.registerPlugins(pluginController, pluginPrevious, pluginNext)
slider.start();
Copy the code

The code involved can be learned in 03.js · Craipy/ youth Camp notes code – code… In the view

Procedural abstraction

  • A method used to handle local detail control
  • Basic application of functional programming ideas

We think of the function itself as a controller, and we care about the inputs and outputs of the controller

To understand:

Operation limit

Let’s take an example. A list of tasks, we click on it to say that the task is done, and then the task fades away, and when it disappears, after deleting the element from the list, we can easily write the code on the right.

This code normally runs fine, but if the same task button is clicked twice in a row, an error is reported.

Because two asynchronous tasks were created, the first one removed the element, and the second one reported an error.

To enable a requirement that is executed once to be covered by different event handlers, we can separate the requirement out, a process we call process abstraction.

So much of what we’ve been talking about, component encapsulation, is really data abstraction, abstracting out data, abstracting out an object, and then passing that object to our plug-in.

In fact process abstraction we can take an action, a function and give it to more tasks to use.

Here’s an example:

We have a person, there is a door, the person has to open the door to enter the room.

If we abstract people out, that’s data abstraction;

In fact, we can also abstract out the action of opening the door, which is process abstraction.

Now, if we do this once, we can write a higher-order function. That is, each function is cleaned up after it is executed once. So that solves the problem of execution once, and it’s also universal.

Higher-order functions

  • Take a function as an argument
  • Take a function as the return value
  • Often used as a function decorator

The basic normal form of higher order function — equivalent normal form

Commonly used higher-order functions

  • Once
  • Throttle – throttling
  • Debounce – stabilization
  • Consumer / 2 — Tasks execute asynchronously
  • Iterative — Iterative

In general, when we have this kind of problem, we don’t want to use higher-order functions. I’m thinking about how to solve these problems in the current function.

Considering only the current function, then we might define the global flag bit. Although this can be achieved, but maybe later, when another function also has such a function, we will use the same method, the same idea to write again.

But to solve this problem, when we look at the function as a whole, and the whole thing doesn’t change, we can write another function to call the original function

Like this, if it’s just a normal call, it doesn’t scale very well. If we call functions as parameters, we can extend this operation to all functions.

Programming paradigm

In conclusion, we can summarize programming into two paradigms

  • imperative
  • declarative

What does the imperative prefer to do

What does the declarative do more

Here’s an example

Toggle – imperative

Toggle – declarative

Toggle – three states

What should you focus on when writing code

  • style
  • The efficiency of
  • convention
  • Usage scenarios
  • design