首页 > 解决方案 > Is it okay to store an Object on a DOM element in a vanilla JS plugin?

问题描述

I'm converting jQuery plugins to Vanilla Javascript. I've adopted a Bootstrap-style plugin structure in my example. Once I've instantiated the Accordion object I save it to the dom element so I can later use various methods on the object. Is this an anti-pattern? I would really appreciate any suggestions on how to handle this if it's not correct.

const Accordion = function(element, options){
    this.$element = element;
    this.target = this.$element.getAttribute('href') || this.$element.dataset.target;
    this.$target = document.getElementById(this.target);
    this.$header = document.querySelector(`.accordion-header[data-target='${this.target}']`) || document.querySelector(`.accordion-header[href='${this.target}']`);
    this.options = {...Accordion.defaults, ...options};
}

Accordion.defaults = {
    closeOthers: false
}

Accordion.prototype.open = function(){
    this.$header.classList.add('active');
    this.$target.classList.add('active');
};
Accordion.prototype.close = function(){
    this.$header.classList.remove('active');
    this.$target.classList.remove('active');
};

Accordion.prototype.toggle = function(){
    if(this.$target.classList.contains('active')){
        this.close();   
    } else {
        this.open();
    }
};

function Plugin(options){
    let accordion = this.Accordion;
    if(!accordion){    
        accordion = new Accordion(this, options);
        // Is it okay to store an object on the DOM element?
        this.Accordion = accordion;
    }
}

const $accordions = [...document.querySelectorAll('[data-toggle="accordion"]')];
const options = {
    closeOthers: true
};

/* Call the plugin */

$accordions.forEach($acc => {
    Plugin.call($acc, options);
});

$accordions.forEach($acc => {
    $acc.addEventListener('click', e => {
        e.target.Accordion.toggle();
        e.preventDefault();       
    });
});

const $toggle1 = document.getElementById('toggle1');
const $toggle2 = document.getElementById('toggle2');
const $acc1 = document.getElementById('acc1');    
const $acc2 = document.getElementById('acc2');    

$toggle1.addEventListener('click', e =>{
    $acc1.Accordion.toggle();
});

$toggle2.addEventListener('click', e =>{
    $acc2.Accordion.toggle();
});
body {
    max-width: 500px;
    margin: 0 auto;
    padding: 10px;
}
.accordion {
    border: 1px solid #ccc;
    border-bottom: 0;
    margin-bottom: 10px;
}

.accordion-header {
    display: block;
    padding: 10px;
    text-decoration: none;
    color: #000;
    border-bottom: 1px solid #ccc;

}
.accordion-header.active {
    background: #eee;
}

.accordion-body {
    padding: 10px;
    display: none;
    border-bottom: 1px solid #ccc;
}

.accordion-body.active {
    display: block;
}
<div class="accordion">
    <a id="acc1" href="#1" class="accordion-header" data-toggle="accordion">Header1</a>
    <div id="#1" class="accordion-body">
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Consequatur aliquid et ipsam cupiditate. Omnis iste quas nostrum aliquid facilis ut natus excepturi deleniti nobis in similique, ex, voluptatibus commodi dolores.
    </div>
</div>

<div class="accordion">
    <a id="acc2" href="#2" class="accordion-header" data-toggle="accordion">Header2</a>
    <div id="#2" class="accordion-body">
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Consequatur aliquid et ipsam cupiditate. Omnis iste quas nostrum aliquid facilis ut natus excepturi deleniti nobis in similique, ex, voluptatibus commodi dolores.
    </div>
</div>

<button id="toggle1">
    Toggle 1
</button>
<button id="toggle2">
    Toggle 2
</button>

标签: javascriptdomplugins

解决方案


我不会担心的。唯一的潜在问题是名称冲突(其他一些代码也尝试分配element.Accordion并覆盖它)。您也可以使用Map.

var accordions = new Map()
function Accordion ( element ) {
    accordions.set( element, this )
}
Accordion.get = element => accordions.get( element )
...
var accordion = Accordion.get( element )

推荐阅读