Snugug / eq.js

Lightweight JavaScript powered element queries
http://eqjs.io/
MIT License
527 stars 34 forks source link

enquire.js-style match/unmatch events #63

Closed jonscottclark closed 8 years ago

jonscottclark commented 8 years ago

Hey @Snugug,

I was about to use enquire.js to enable some plugin on a component over a certain breakpoint, and disable the plugin when it gets too small, and I realized that since the component is eq.js-enabled, and will be re-used in a sidebar context, that enquire.js doesn't make sense any more.

I wrote this little stub (with jQuery as a dependency) to handle this case, and wanted to share it. It's probably a little too much overhead in order to keep this plugin lightweight and focused on one thing, but I feel like I'll be reusing this code, and it fills a void that exists when you need to respond to element queries in your JS.

let breakpointToMatch = 'small';

$('#element').on('eqResize', function(e) {
  let eq = e.originalEvent.detail;
  if (eq === null) {
    return;
  }

  let breakpoints = eq.split(' ');
  let $this = $(this);

  if ($.inArray(breakpointToMatch, breakpoints) !== -1 && !$this.data('matched')) {
    $this.data('matched', true);
    // Do something because we've matched the breakpoint
  }

  if ($.inArray(breakpointToMatch, breakpoints) === -1 && $this.data('matched')) {
    $this.data('matched', false);
    // Do something else because we don't match the breakpoint anymore
  }

  // Otherwise, do nothing, because we haven't changed breakpoints
});

Ideally there could be an eqMatch and an eqUnmatch event that get fired that could leverage the data returned by the eqResize event — but they would need to know about the breakpoint you want to match (only by the name the author has defined for it).

What are your thoughts/feelings about having this type of capability as a feature?

(Thanks for your work on this polyfill, btw. Getting EQs working in about an hour was a pretty awesome experience.)

jonscottclark commented 8 years ago

Here's what I'm using now. Changed some variable names and turned the above into a small utility function accessible to my components' JS.

// Utility function
let eq = function(e, breakpoint, config) {
  let eqValues = e.originalEvent.detail;
  if (eqValues === null) {
    return;
  }

  eqValues = eqValues.split(' ');
  let $this = $(e.target);

  if ($.inArray(breakpoint, eqValues) !== -1 && !$this.data('eq-matched')) {
    $this.data('eq-matched', true);
    config.match(e);
  }

  if ($.inArray(breakpoint, eqValues) === -1 && $this.data('eq-matched')) {
    $this.data('eq-matched', false);
    config.unmatch(e);
  }
}

Whenever a component needs matching/unmatching functionality I can use the much nicer looking declaration which reminds me more of the config for a registered breakpoint with enquire.js

// Call the utility function from an 'eqResize' event handler
$('#element').on('eqResize', function(e) {
  eq(e, 'xsmall', {
    'match': function(e) {
      // Do stuff when matched (including if it matches on page load)
    },
    'unmatch': function(e) {
      // Do stuff when unmatched (but not if it doesn't match on page load),
      // because the `eq-matched` data attribute isn't present on #element
    }
  });
});
Snugug commented 8 years ago

You are correct that I'm not going to bring in jQuery (or have it as a dependency for this project) to support this use case. I'm also not convinced of the utility of a helper function or the syntax provided when the following JavaScript does the same thing in IMO a much more clear way:

var foo = document.getElementById('foo');

foo.addEventListener('eqResize', function (e) {
  var state = e.originalEvent.detail;
  if (state && state.split(' ').indexOf('xsmall') >= 0) {
    // Do Stuff if it's there
  } else {
    // Do Other Stuff if it's not there
  }
}
jonscottclark commented 8 years ago

p.s., I wasn't advocating jQuery as a dependency, it's just part of my project and wanted to demonstrate how I was achieving the use case.

In your example, the code within those if/else blocks will execute on every resize event. The events I'm looking to access are when an element goes from a matched state to an unmatched state, and vice versa. I'm sure there's some more elegant way in JS to mark an element as matched, vs. the workaround of using jQuery's data() function. It could be possible that it's easy to do on a case-by-case basis, or maybe it could be worked into the library to expose a couple of new events.

Anyways, thanks for looking, and thanks again for your work on this :+1: