skip to Main Content

I would like to create a private custom event, an event nobody else can dispatch or listen. I tried this, but it does not work:

new CustomEvent(Symbol("validate"));

Is it possible?

I know I can do something like this:

new CustomEvent(crypto.randomUUID());

2

Answers


  1. Chosen as BEST ANSWER

    This seems to be the maximum, which can be achieved.

    class ObscureEvent {
      #name;
      #event;
      constructor (options) {
        this.#name = crypto.randomUUID();
        this.#event = new CustomEvent(this.#name, options); }
      listen(target, handler) {
        target.addEventListener(this.#name, handler); }
      dispatch(target) {
        target.dispatchEvent(this.#event); } }
          
    const validate = new ObscureEvent({ bubbles: true });
    

  2. From some of the OP’s and my comments …

    Of cause one could build a system which encloses/encapsulates everything except for e.g. an instance’s de/register / un/subscribe methods where one could add/remove objects which each feature an own (event/topic/subject/channel specific) handler function. The dispatching instance does not have a public dispatchEvent method but would invoke each registered/subscribed object’s specific handler function. Something similar to this … – Peter Seliger

    @PeterSeliger The problem with the event-strings is: they share one global name-space. Nothing prevents collisions. – ceving

    Nope … if one [privately] ties an own EventTarget instance to every instance of a type which is capable of dispatching events to selected (validated and registered) objects, no one except the secretly/privately dispatching instance itself is capable of accessing such dispatched events and its related data. This is due to just the dispatching instance itself can access the dispatching object. The instance itself controls the de/registering of objects which each have to be capable of handling such controlled dispatched (and otherwise not accessible) event-types. – Peter Seliger

    const subscriber_1 = {
      uuid: crypto.randomUUID(),
      handleTypeA(evt) {
        console.log({
          eventType: evt.type,
          currentTarget: evt.currentTarget,
          targetConstructorName: evt.detail.target.constructor.name,
          subscriber: subscriber_1,
          payload: evt.detail.payload,
        });
      }
    };
    const subscriber_2 = {
      uuid: crypto.randomUUID(),
      handleTypeB(evt) {
        console.log({
          eventType: evt.type,
          currentTarget: evt.currentTarget,
          targetConstructorName: evt.detail.target.constructor.name,
          subscriber: subscriber_2,
          payload: evt.detail.payload,
        });
      }
    };
    const subscriber_3 = {
      uuid: crypto.randomUUID(),
      handleTypeB(evt) {
        console.log({
          eventType: evt.type,
          currentTarget: evt.currentTarget,
          targetConstructorName: evt.detail.target.constructor.name,
          subscriber: subscriber_3,
          payload: evt.detail.payload,
        });
      },
      handleTypeZ(evt) {
        console.log({
          eventType: evt.type,
          currentTarget: evt.currentTarget,
          targetConstructorName: evt.detail.target.constructor.name,
          subscriber: subscriber_3,
          payload: evt.detail.payload,
        });
      }
    };
    const sampleInstance = new PrivatlyDispatchingType;
    
    console.log(
      'sampleInstance.addSubscribers(subscriber_1, subscriber_2, subscriber_3) ...n',
      '... sampleInstance.aggregatesAndDispatchesSampleData() ...',
    );
    sampleInstance.addSubscribers(subscriber_1, subscriber_2, subscriber_3);
    sampleInstance.aggregatesAndDispatchesSampleData();
    
    console.log(
      'sampleInstance.removeSubscribers([subscriber_1, subscriber_3]) ...n',
      '... sampleInstance.aggregatesAndDispatchesSampleData() ...',
    );
    sampleInstance.removeSubscribers([subscriber_1, subscriber_3]);
    sampleInstance.aggregatesAndDispatchesSampleData();;
    
    console.log(
      'sampleInstance.removeSubscribers(subscriber_2) ...n',
      '... sampleInstance.aggregatesAndDispatchesSampleData() ...',
    );
    sampleInstance.removeSubscribers(subscriber_2);
    sampleInstance.aggregatesAndDispatchesSampleData();
    .as-console-wrapper { min-height: 100%!important; top: 0; }
    <script>
    // scope of module ... `PrivatlyDispatchingType`
    
    function isValidSubscriber(value) {
      // e.g. implement other custom type check functionality.
      return (
        (typeof value?.uuid === 'string') &&
        (Object
          .entries(value)
          .filter(([key, value]) =>
            key.startsWith('handle') && (typeof value === 'function')
          )
          .length >= 1)      
      );
    }
    
    function getValidSubscribers(...subscribers) {
      return subscribers.flat().filter(isValidSubscriber);
    }
    
    const eventTypeLookup = {
      handleTypeA: 'private-event-type-A',
      handleTypeB: 'private-event-type-B',
      handleTypeZ: 'private-event-type-Z',
    }
    
    class PrivatlyDispatchingType {
      #dispatcher = new EventTarget;
      #subscribers = new Map;
    
      /**
       *  type specific implementation
       */
    
      //  ----- ----- ----- ----- -----
    
      addSubscribers(...args) {
        getValidSubscribers(...args)
    
          .forEach(subscriber => {
            const { uuid } = subscriber;
    
            if (!this.#subscribers.has(uuid)) {
              this.#subscribers.set(uuid, subscriber);
    
              Object
                .entries(subscriber)
                .filter(([key, value]) =>
                  key.startsWith('handle') && (typeof value === 'function')
                )
                .forEach(([handlerName, handlerFunction]) =>
    
                  this.#dispatcher.addEventListener(
                    eventTypeLookup[handlerName], handlerFunction
                  )
                );
            }
          });
      }
      removeSubscribers(...args) {
        getValidSubscribers(...args)
    
          .forEach(subscriber => {
            const { uuid } = subscriber;
    
            if (this.#subscribers.has(uuid)) {
              this.#subscribers.delete(uuid);
    
              Object
                .entries(subscriber)
                .filter(([key, value]) =>
                  key.startsWith('handle') && (typeof value === 'function')
                )
                .forEach(([handlerName, handlerFunction]) =>
    
                  this.#dispatcher.removeEventListener(
                    eventTypeLookup[handlerName], handlerFunction
                  )
                );
            }
          });
      }
    
      aggregatesAndDispatchesSampleData() {
        this.#dispatcher.dispatchEvent(new CustomEvent(
          'private-event-type-A', {
            detail: {
              target: this,
              payload: {
                foo: 'Foo',
                bar: 'Bar',
              }
            }
          })
        );
        this.#dispatcher.dispatchEvent(new CustomEvent(
          'private-event-type-B', {
            detail: {
              target: this,
              payload: {
                baz: 'Baz',
                biz: 'Biz',
              }
            }
          })
        );
        this.#dispatcher.dispatchEvent(new CustomEvent(
          'private-event-type-Z', {
            detail: {
              target: this,
              payload: {
                quick: 'quick',
                brown: 'brown',
                fox: 'fox',
              }
            }
          })
        );
      }
    }
    </script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search