te')); return $arr; } /* 遍历用户所有主题 * @param $uid 用户ID * @param int $page 页数 * @param int $pagesize 每页记录条数 * @param bool $desc 排序方式 TRUE降序 FALSE升序 * @param string $key 返回的数组用那一列的值作为 key * @param array $col 查询哪些列 */ function thread_tid_find_by_uid($uid, $page = 1, $pagesize = 1000, $desc = TRUE, $key = 'tid', $col = array()) { if (empty($uid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('uid' => $uid), array('tid' => $orderby), $page, $pagesize, $key, $col); return $arr; } // 遍历栏目下tid 支持数组 $fid = array(1,2,3) function thread_tid_find_by_fid($fid, $page = 1, $pagesize = 1000, $desc = TRUE) { if (empty($fid)) return array(); $orderby = TRUE == $desc ? -1 : 1; $arr = thread_tid__find($cond = array('fid' => $fid), array('tid' => $orderby), $page, $pagesize, 'tid', array('tid', 'verify_date')); return $arr; } function thread_tid_delete($tid) { if (empty($tid)) return FALSE; $r = thread_tid__delete(array('tid' => $tid)); return $r; } function thread_tid_count() { $n = thread_tid__count(); return $n; } // 统计用户主题数 大数量下严谨使用非主键统计 function thread_uid_count($uid) { $n = thread_tid__count(array('uid' => $uid)); return $n; } // 统计栏目主题数 大数量下严谨使用非主键统计 function thread_fid_count($fid) { $n = thread_tid__count(array('fid' => $fid)); return $n; } ?>javascript - CustomEvent listener callback is firing twice? - Stack Overflow
最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - CustomEvent listener callback is firing twice? - Stack Overflow

programmeradmin3浏览0评论

I've created a custom element:

const templ = document.createElement('template');
templ.innerHTML = `
<span><slot></slot></span>
`;
class SlideButton extends HTMLElement {

    constructor() {
        super();

        // Attach a shadow root to the element.
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.appendChild(tmpl.content.cloneNode(true));
        this.span = shadowRoot.querySelector('span');

        this.triggerEvent = new CustomEvent("trigger", {
            bubbles: false,
            cancelable: false,
        });

        this.initMouseEvents();
    }

    initMouseEvents() {
        this.span.addEventListener('mousedown', (e) => {

            //Watch and calculate slide amount..
            this.addEventListener('mousemove', this.slide, false);

        });

        //When button is released...
        document.addEventListener('mouseup', handleMouseUp = (e) => {

            this.removeEventListener('mousemove', this.slide, false);
            document.removeEventListener('mouseup', handleMouseUp);

            //If slided enough, dispatch event...
            if (Math.abs(this.slideAmount) > (this.maxSlide * 0.75)) {
                console.log('firing event');
                this.dispatchEvent(this.triggerEvent);
            }
            //Reset button to normal state...

        }, false);
    }
}

Somewhere else in my code..

class SpotLightModal {

    //Constructor..
    //code..
    //code..

    init() {
        this.actions.querySelector('slide-button[type="den"]').addEventListener('trigger', e => {
            console.log(e);
            console.log('den');
            //Do stuff..
        });
    }

    //code...
    //code...

}

Everything works as expected except that the callback in the event listener is runs twice and the output is:

firing event
CustomEvent {...}
den
CustomEvent {...}
den

Both e.stopPropagation() and e.preventDefault() have no effect and trying to use them did nothing..

I've edited to include this.span and also moved the "mouseup" event listener outside the "mousedown" event listener but that didn't work, infact when logging this, now, it gives another different element (of the same kind, <slide-button>, the first on the page), the "mouseover" listener doesn't get removed, and the event isn't fired.

Am I doing something wrong in here? or what am I missing exactly?

Thanks in advance..

I've created a custom element:

const templ = document.createElement('template');
templ.innerHTML = `
<span><slot></slot></span>
`;
class SlideButton extends HTMLElement {

    constructor() {
        super();

        // Attach a shadow root to the element.
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.appendChild(tmpl.content.cloneNode(true));
        this.span = shadowRoot.querySelector('span');

        this.triggerEvent = new CustomEvent("trigger", {
            bubbles: false,
            cancelable: false,
        });

        this.initMouseEvents();
    }

    initMouseEvents() {
        this.span.addEventListener('mousedown', (e) => {

            //Watch and calculate slide amount..
            this.addEventListener('mousemove', this.slide, false);

        });

        //When button is released...
        document.addEventListener('mouseup', handleMouseUp = (e) => {

            this.removeEventListener('mousemove', this.slide, false);
            document.removeEventListener('mouseup', handleMouseUp);

            //If slided enough, dispatch event...
            if (Math.abs(this.slideAmount) > (this.maxSlide * 0.75)) {
                console.log('firing event');
                this.dispatchEvent(this.triggerEvent);
            }
            //Reset button to normal state...

        }, false);
    }
}

Somewhere else in my code..

class SpotLightModal {

    //Constructor..
    //code..
    //code..

    init() {
        this.actions.querySelector('slide-button[type="den"]').addEventListener('trigger', e => {
            console.log(e);
            console.log('den');
            //Do stuff..
        });
    }

    //code...
    //code...

}

Everything works as expected except that the callback in the event listener is runs twice and the output is:

firing event
CustomEvent {...}
den
CustomEvent {...}
den

Both e.stopPropagation() and e.preventDefault() have no effect and trying to use them did nothing..

I've edited to include this.span and also moved the "mouseup" event listener outside the "mousedown" event listener but that didn't work, infact when logging this, now, it gives another different element (of the same kind, <slide-button>, the first on the page), the "mouseover" listener doesn't get removed, and the event isn't fired.

Am I doing something wrong in here? or what am I missing exactly?

Thanks in advance..

Share Improve this question edited Sep 16, 2019 at 18:38 Nasa asked Sep 16, 2019 at 15:59 NasaNasa 4195 silver badges15 bronze badges 2
  • I think this is happening because you are nesting events within events. – Chris Hemmens Commented Sep 16, 2019 at 16:03
  • @ChrisHemmens I tried ordering them differently but nothing worked.. other orders cause the event to fir multiple times or not at all – Nasa Commented Sep 16, 2019 at 16:07
Add a ment  | 

4 Answers 4

Reset to default 14

If anyone else is having a similar problem try using:

event.stopImmediatePropagation() into your callback function like so

window.addEventListener('message', (event) => {
  event.stopImmediatePropagation();
  console.log('detail:', event.detail);
})

In my case this seemed to do the trick, but it's definitely super hacky and would remend trying to find out the root cause of your issue if it needs to be super reliable.

https://developer.mozilla/en-US/docs/Web/API/Event/stopImmediatePropagation

In my case, I was using event bus to dispatch custom event which will trigger a callback function in which the double events happened.

Initially, the custom event listener was in the constructor()

constructor() {
    EventBus.addEventListener('someEvent', this.doSomething);
}

And I stopped seeing double events once I moved that line to connectedCallback():

connectedCallback() {
   EventBus.addEventListener('someEvent', this.doSomething);
}

The problem lies in the nesting of your code.

First you add the first mousedown event listener which will trigger on when the mouse is clicked and not released.

this.span.addEventListener('mousedown', (e) => { ...

Then inside of your mousedown event listener you listen for the mouseup event on the document.

document.addEventListener('mouseup', handleMouseUp = (e) => { ...

So now, whenever you click both the mousedown and the mouseup will be triggered. Even when you remove the event listener inside the mouseup event listener. The code inside there is already executing.
That is what causes your code to fire the CustomEvent twice.

Prevent the nesting of event listeners, except when one would rely on the other. Like in the mousedown event listener where you need to add the mousemove event listener. Now instead of nesting add another event listener that listens to mouseup adjecent to the mousedown event listener. Remove the mousemove event there. Example below:

class SlideButton extends HTMLElement {

    constructor() {
        super();

        // Attach a shadow root to the element.
        const shadowRoot = this.attachShadow({ mode: 'open' });
        shadowRoot.appendChild(tmpl.content.cloneNode(true));
        this.span = shadowRoot.querySelector('span');

        this.triggerEvent = new CustomEvent("trigger", {
            bubbles: false,
            cancelable: false,
        });

    }

    connectedCallback() {

        // It's preferred to set the event listeners in the connectedCallback method.
        // This method is called whenever the current element is connected to the document.
        this.initMouseEvents();

    }

    initMouseEvents() {

        // Bind slider-button as this context to the slide method.
        // In case you use the this keyword inside of the slide method
        // you would need to do this to keep using the methods of this class.
        const slide = this.slide.bind(this);

        // Create a toggle to indicate that the slide-button has been clicked so that ONLY then the event listeners will be added.
        let targetIsClicked = false;

        // Mouse is clicked, mousemove added.
        document.addEventListener('mousedown', (e) => {

            // Check if this button has been clicked.
            const slideButton = e.target.closest('slide-button');
            if (slideButton === this) {

                // Toggle true and add mousemove event.
                targetIsClicked = true;
                document.addEventListener('mousemove', slide, false);
            }

        });

        // Mouse is released, remove mousemove and fire event.
        document.addEventListener('mouseup', (e) => {

            // Only do something if this button has been clicked in the mousedown event.
            if (targetIsClicked === true) {
                document.removeEventListener('mousemove', slide, false);

                // Reset toggle value.
                targetIsClicked = false;

                //If slided enough, dispatch event...
                if (Math.abs(this.slideAmount) > (this.maxSlide * 0.75)) {
                    console.log('firing event');
                    this.dispatchEvent(this.triggerEvent);
                }
                //Reset button to normal state...

            }

        });

    }
}

Edit

I've changed the event listener's target to document. You explained that you want the mousemove and mouseup events to work when triggering them outside the button.

In the mousedown I check if the current target that has been clicked is in fact this button. And if so the targetIsClicked value will toggled to indicate that the correct button has been clicked.

In the mouseup event first check if the targetIsClicked is true. Meaning you clicked this button and execute the rest of your code.

Although, if you'd have multiple <slide-button> elements there would be multiple mousedown, mousemove and mouseup listeners attached to the document, which could create some weird outes.

Note

I've moved the this.initMouseEvents() function call to the connectedCallback() method. It is good practice to do this because now you are interacting with the lifecycle of the custom element. Add the event listeners in connectedCallback() when the element is created and remove event listeners in disconnectedCallback() when the element is removed. The element itself triggers these methods, so you don't have to call them.

There's a "bubbles" option that you can set to false, which should eliminate the mouse-down/up bubbling phases:

var evt = new CustomEvent(name, {
    detail : data,
    bubbles: true, // <-- try setting to false
    cancelable: true
});

document.dispatchEvent(evt);
发布评论

评论列表(0)

  1. 暂无评论