简体   繁体   English

class 更改上的事件触发器

[英]Event trigger on a class change

I'd like my event to be triggered when a div tag containing a trigger class is changed.我希望在更改包含触发器class 的 div 标签时触发我的事件。

I have no idea how to make it listen to the class' adding event.我不知道如何让它监听班级的添加事件。

<div id="test">test</div>

<script type="text/javascript">
    document.getElementById.setAttribute("class", "trigger");

    function workOnClassAdd() {
       alert("I'm triggered");
    }
</script>

The future is here, and you can use the MutationObserver interface to watch for a specific class change.未来就在这里,您可以使用MutationObserver接口来监视特定的类更改。

let targetNode = document.getElementById('test')

function workOnClassAdd() {
    alert("I'm triggered when the class is added")
}

function workOnClassRemoval() {
    alert("I'm triggered when the class is removed")
}

// watch for a specific class change
let classWatcher = new ClassWatcher(targetNode, 'trigger', workOnClassAdd, workOnClassRemoval)

// tests:
targetNode.classList.add('trigger') // triggers workOnClassAdd callback
targetNode.classList.add('trigger') // won't trigger (class is already exist)
targetNode.classList.add('another-class') // won't trigger (class is not watched)
targetNode.classList.remove('trigger') // triggers workOnClassRemoval callback
targetNode.classList.remove('trigger') // won't trigger (class was already removed)
targetNode.setAttribute('disabled', true) // won't trigger (the class is unchanged)

I wrapped MutationObserver with a simple class:我用一个简单的类包装了MutationObserver

class ClassWatcher {

    constructor(targetNode, classToWatch, classAddedCallback, classRemovedCallback) {
        this.targetNode = targetNode
        this.classToWatch = classToWatch
        this.classAddedCallback = classAddedCallback
        this.classRemovedCallback = classRemovedCallback
        this.observer = null
        this.lastClassState = targetNode.classList.contains(this.classToWatch)

        this.init()
    }

    init() {
        this.observer = new MutationObserver(this.mutationCallback)
        this.observe()
    }

    observe() {
        this.observer.observe(this.targetNode, { attributes: true })
    }

    disconnect() {
        this.observer.disconnect()
    }

    mutationCallback = mutationsList => {
        for(let mutation of mutationsList) {
            if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
                let currentClassState = mutation.target.classList.contains(this.classToWatch)
                if(this.lastClassState !== currentClassState) {
                    this.lastClassState = currentClassState
                    if(currentClassState) {
                        this.classAddedCallback()
                    }
                    else {
                        this.classRemovedCallback()
                    }
                }
            }
        }
    }
}

Well there were mutation events , but they were deprecated and the future there will be Mutation Observers, but they will not be fully supported for a long time.嗯,有突变事件,但它们已被弃用,未来会有突变观察者,但在很长一段时间内不会得到完全支持。 So what can you do in the mean time?那么在此期间你能做什么呢?

You can use a timer to check the element.您可以使用计时器来检查元素。

function addClassNameListener(elemId, callback) {
    var elem = document.getElementById(elemId);
    var lastClassName = elem.className;
    window.setInterval( function() {   
       var className = elem.className;
        if (className !== lastClassName) {
            callback();   
            lastClassName = className;
        }
    },10);
}

Running example: jsFiddle运行示例: jsFiddle

Here's a simple, basic example on how to trigger a callback on Class attribute change这是一个简单的基本示例,说明如何在 Class 属性更改时触发回调
MutationObserver API MutationObserver API

 const attrObserver = new MutationObserver((mutations) => { mutations.forEach(mu => { if (mu.type !== "attributes" && mu.attributeName !== "class") return; console.log("class was modified!"); }); }); const ELS_test = document.querySelectorAll(".test"); ELS_test.forEach(el => attrObserver.observe(el, {attributes: true})); // Example of Buttons toggling several .test classNames document.querySelectorAll(".btn").forEach(btn => { btn.addEventListener("click", () => ELS_test.forEach(el => el.classList.toggle(btn.dataset.class))); });
 .blue {background: blue;} .gold {color: gold;}
 <div class="test">TEST DIV</div> <button class="btn" data-class="blue">BACKGROUND</button> <button class="btn" data-class="gold">COLOR</button>

I needed a class update listener for a project, so I whipped this up.我需要一个项目的类更新监听器,所以我做了这个。 I didn't end up using it, so it's not fully tested, but should be fine on browsers supporting Element.classList DOMTokenList.我最终没有使用它,所以它没有经过全面测试,但在支持 Element.classList DOMTokenList 的浏览器上应该没问题。

Bonus: allows “chaining” of the 4 supported methods, for example el.classList.remove(“inactive”).remove(“disabled”).add(“active”)奖励:允许“链接”4 个支持的方法,例如el.classList.remove(“inactive”).remove(“disabled”).add(“active”)

function ClassListListener( el ) {
  const ecl = el.classList;
  ['add','remove','toggle','replace'].forEach(prop=>{
    el.classList['_'+prop] = ecl[prop]
    el.classList[prop] = function() {
      const args = Array.from(arguments)
      this['_'+prop].apply(this, args)
      el.dispatchEvent(new CustomEvent(
        'classlistupdate',
        { detail: { method: prop, args } }
      ))
      return this
    }
  })
  return el
}

Useage:用途:


const el = document.body

ClassListListener(el).addEventListener('classlistupdate', e => {
    const args = e.detail.args.join(', ')
    console.log('el.classList.'+e.detail.method+'('+args+')')
}, false)

el.classList
  .add('test')
  .replace('test', 'tested')

Can use this onClassChange function to watch whenever classList of an element changes可以使用这个onClassChange函数来观察元素的classList何时发生变化

 function onClassChange(element, callback) { const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if ( mutation.type === 'attributes' && mutation.attributeName === 'class' ) { callback(mutation.target); } }); }); observer.observe(element, { attributes: true }); return observer.disconnect; } var itemToWatch = document.querySelector('#item-to-watch'); onClassChange(itemToWatch, (node) => { node.classList.contains('active') ? alert('class added') : alert('class removed'); node.textContent = 'Item to watch. classList: ' + node.className; }); function addClass() { itemToWatch.classList.add('active'); } function removeClass() { itemToWatch.classList.remove('active'); }
 <div id="item-to-watch">Item to watch</div> <button onclick="addClass();">Add Class</button> <button onclick="removeClass();">Remove Class</button>

The idea is to substitute class manipulation functions, such as 'add', 'remove'... with wrappers, that send class change messages before or after class list changed.这个想法是用包装器替换 class 操作函数,例如“添加”、“删除”...,这些函数在 class 列表更改之前或之后发送 class 更改消息。 It's very simple to use:使用非常简单:

  1. choose element(s) or query that selects elements, and pass it to the function.选择元素或选择元素的查询,并将其传递给 function。

  2. add 'class-change' and/or 'class-add', 'class-remove'... handlers to either elements or their container ('document', for example).将 'class-change' 和/或 'class-add'、'class-remove'... 处理程序添加到元素或其容器(例如,'document')。

  3. after that, any class list change by either add, remove, replace or toggle methods will fire corresponding events.之后,任何通过添加、删除、替换或切换方法更改的 class 列表都会触发相应的事件。

Event sequence is:事件顺序为:

A) 'class-change' request event is fired, that can be rejected by handler by preventDefault() if needed. A) 'class-change' 请求事件被触发,如果需要,处理程序可以通过 preventDefault() 拒绝该事件。 If rejected, then class change will be cancelled.如果被拒绝,则 class 更改将被取消。

B) class change function will be executed B) class 更改 function 将被执行

B) 'class-add' or 'class-remove'... information event is fired. B) 'class-add' 或 'class-remove'... 信息事件被触发。

function addClassChangeEventDispatcher( el )
{
    // select or use multiple elements
    if(typeof el === 'string') el = [...document.querySelectorAll( el )];
    
    // process multiple elements
    if(Array.isArray( el )) return el.map( addClassChangeEventDispatcher );
    
    // process single element
    
    // methods that are called by user to manipulate
    let clMethods = ['add','remove','toggle','replace'];
    
    // substitute each method of target element with wrapper that fires event after class change
    clMethods.forEach( method =>
    {
        let f = el.classList[method];       
        el.classList[method] = function() 
        {
            // prepare message info
            let detail = method == 'toggle'  ? { method, className: arguments[0] } : 
                        method == 'replace' ? { method, className: arguments[0], newClassName: arguments[1] } : 
                                                                                 { method, className: arguments[0], classNames: [...arguments] };           
            
            // fire class change request, and if rejected, cancel class operation 
            if(!el.dispatchEvent( new CustomEvent( 'class-change', {bubbles: true, cancelable: true, detail} ))) return;
            
            // call original method and then fire changed event
            f.call( this, ...arguments );
            el.dispatchEvent( new CustomEvent( 'class-' + method, {bubbles: true, detail} ));
        };
    });
    
    return el;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM