Giter VIP home page Giter VIP logo

metaquery's Introduction

metaquery

A declarative responsive web design syntax. Breakpoints, defined in <meta> With metaquery, you define your media query breakpoints once, and only once.

(Demo)

— by @benschwarz

Getting Started

Install via Bower bower install metaquery

Otherwise, if you want to grab it manually:

Download the production version (416 bytes) or the development version.

---

  • Define your breakpoints in <meta> tags.
<meta name="breakpoint" content="phone" media="(max-width: 480px)">
<meta name="breakpoint" content="small-tablet" media="(min-width: 480px) and (max-width: 600px)">
<meta name="breakpoint" content="tablet" media="(min-width: 600px) and (max-width: 1024px)">
<meta name="breakpoint" content="widescreen" media="(min-width: 1024px)">
<meta name="breakpoint" content="retina" media="only screen and (-webkit-min-device-pixel-ratio : 2)">
  • metaQuery will add/remove classes to the <html> node (.breakpoint-<name-of-breakpoint>) for you to utilise when the breakpoints are matched. (shorter than media queries. don't repeat yourself)
<style>
  .breakpoint-phone { background: red; }
  .breakpoint-small-tablet { background: green; }
  .breakpoint-tablet { background: blue; }
  .breakpoint-widescreen { background: yellow; }
</style>
  • Responsive images in one simple line.
<img src="./images/phone.jpg" data-mq-src="./images/[breakpoint].jpg">
Modernizr.load([{
  test: ( !!window.matchMedia ),
  nope: ['./js/vendor/matchMedia-oldie.js']
}]);

Adding your own javascript events with metaQuery.onBreakpointChange

Considering the HTML example above, say you wanted watch for 'phone' breakpoint changes:

metaQuery.onBreakpointChange( 'phone', function ( match ) {
  if( match ) { // phawor! your media query matches. }
});

and if you just want to fire an event whenever you switch breakpoints (but don't care which)

metaQuery.onBreakpointChange( function (activeBreakpoints) {
    // do something amazing because you've changed breakpoints!
    // your callback will also be passed an array containing the names of active breakpoints.
});

Browser support

metaQuery requires matchMedia:

Otherwise, metaQuery will work with all common desktop and mobile browsers.

Browserify / CJS

metaQuery can be used with browserify.

Backstory

In 2011/12 I worked on a large HTML magazine that is edited by an editorial team. Each 'module' of a template is responsive, and requires unique styles and sometimes even scripts. After reflecting on the project for some time, what worked and what didn't, I made some simple observations:

  • Writing media queries over and over again sucks. (Even though I use the technique that I illustrated back in December 11')
  • If you want media query access in javascript, you'll create yet another media query with matchMedia
  • picturefill is the best responsive image technique I've seen… until I authored templates for a massive magazine. The syntax is too long and easy to forget.
  • and finally, a summary of all three: a declarative interface is far nicer to use.

After reading both Jeremy Keith and Matt Wilcox's articles, then the source of picturefill I decided to get my hands dirty and have a go at a slightly better approach.

Contributing

Please use idiomatic.js as a styleguide and take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using grunt.

Thanks to

Reviewers: Tim Lucas, Ben Alman, Jeremy Keith, Paul Irish, Divya Manian, David Desandro, Nicolas Gallagher and Mat Marquis

Code:

  • Scott Jehl for writing picturefill, matchMedia.js (with Paul Irish) and respond.js. Legend.
  • Dustin Diaz's teeny dom ready.

License

Copyright (c) 2012 Ben Schwarz Licensed under the MIT license.

metaquery's People

Contributors

benschwarz avatar caseyleask avatar geelen avatar joho avatar jonathantneal avatar plasticine avatar tbranyen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metaquery's Issues

onBreakpointChange acting as window.resize

Using the following, breakpoint agnostic, syntax the handler is fired continuously on resize instead of being fired solely on breakpoint change It should behave correctly with the breakpoint naming syntax.

metaQuery.onBreakpointChange( function (activeBreakpoints) {
// gets called on resize instead of just once on breakpoint change
});

The solution I have to use for now is to set flags but it's a workaround that we shouldn't be facing thanks to metaQuery.

Unprefix min-device-pixel-ratio

This needs to be done within the script to add prefixes. device pixel ratio applies not just for Webkit but for other rendering engines as well.

Website

Create a website showing the API, download options etc etc.

Time to make this bad boy sing.

Delay before correct style applied

Hi Ben,

I did a design with 4 breakpoints, very clean solution thanks

but... I have an issue...

The (correct) style needs to wait for the breakpoint class to be added to html element, the default style is applied the time it takes for the js to be loaded

It result with an annoying switch between what I call the default style and the correct one.

By default style I mean the one applied if there is no breakpoint class in the html element.

Not working with Browserify - Needs to be re-uglified

Thanks for sharing this and thanks for making it work with Browserify.

However, when using Browserify with browserify-shim and debowerify (installing metaquery through bower and using it in Browserify) it's currently broken because the shim looks into .bower.json which targets the already uglified metaquery.min.js which has not been re-uglified since the latest changes that make this repository work with Browserify.

updateClasses mashes preexistent classnames together

I'm using your fine script in a situation where the html element loads with another classname, inserted elsewhere programmatically. When metaquery updates its classes, it mistakenly adds its classes to the preexistent class as well. For example:

<html class="js">

becomes

<html class="js breakpoint-desktop"> 

but then

<html class="jsbreakpoint-phone breakpoint-phone"> 

etc

Error "Object expected" in IE8 using metaquery.min.js and modified matchMedia polyfill

Hello,

I can't seem to get this working in IE8, despite calling the modified matchMedia polyfill through Modernizr.
I get a "Object Expected" error on ligne 48 of the non-minified version of metaquery.js, where there is func.apply( thisArg, args );.
The same goes for the jQuery version, where the line is then 28 but on the same element (func.apply( thisArg, args );).
I hope this is clear...

-- Edit (13/11/03): replacing line 42 of metaquery.js var args, by var args = [], does shut IE up but alas it doesn't make it work any better? --

Is this a know bug? Or am I doing something wrong?

I'm working on getting this online where you can see it (the client is rather sensitive), in the meantime here is the JS code applied in the document.ready function at the end of my main.js file (after everything else, inc. zepto/jquery and modernizr has loaded):

// conditionnaly load and init polyfills
    Modernizr.load([
        // responsive images
        {
          test: !!window.matchMedia,
          nope: './js/vendor/matchMedia.js',
          load: './js/plugins/metaquery.min.js',
        }
    ]);

-- Edit (13/11/03): I just realized I hadn't properly pasted my code here, as the test condition was missing its !! at the beginning. Corrected.
I have, however, little to no hope of being able to provide a live example. Still working on it but any meantime help is appreciated. --

Thanks a lot if you have any idea why this is!!

Suggestion for naming breakpoints

My colleague wants to be able to pass an option to set the "breakpoint-" prefix as she likes to use all caps class names for the media queries, which to be honest is not a bad idea as it visually identifies the media query.

To continue the configuration paradigm perhaps it could be a meta tag:
<meta name="breakpoint-prefix" content="BP_" >

It could also allow us to use such values as @media-. This will be targeted in css with .\@media-tablet .selector { } making it immediately recognisable as a (non-standard) media query.

onBreakpointChange overrides itself

Using the following syntax multiple times, with the same breakpoint name (for example from different functions or modules), doesn't give expected result as metaQuery.onBreakpointChange is only fired once and not for each handler. In that specific case, it seems like defining 2 or more times an onBreakpointChange declaration overrides the previous one.

metaQuery.onBreakpointChange( 'phone', function ( match ) {
if( match ) { }
});

better hasClass / removeClass / addClass functions?

You don't expose these functions such that they can be unit tested, so I just threw something together:

var classRegex = {};
var getClassRegex = function(className) {
  if (!classRegex[className]) {
    classRegex[className] = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g');
  }
  classRegex[className].lastIndex = 0;
  return classRegex[className];
};

var hasClass = function(element, className) {
  return getClassRegex(className).test(element.className);
};

var removeClass = function(element, className) {
  element.className = (element.className || '').replace(getClassRegex(className), '');
};

var addClass = function(element, className) {
  if (!hasClass(element, className)) {
    element.className = (element.className ? element.className + ' ' : '') + className;
  }
};

[
  null,
  '',
  'foo',
  'bar',
  'foo bar',
  'bar foo',
  'a-foo bar',
  'foo-a bar',
  'bar a-foo',
  'bar foo-a',
  'foo-a',
  'a-foo',
  'a-foo-b',
  '  foo  bar  foo  '
].forEach(function(className) {
  var element = {};
  var myClass = 'foo';
  console.log('=== className: "%s" ===', className);

  element.className = className;
  console.log('hasClass? %d', hasClass(element, myClass));

  element.className = className;
  removeClass(element, myClass);
  console.log('removeClass "%s" -> "%s"', className, element.className);

  element.className = className;
  addClass(element, myClass);
  console.log('addClass "%s" -> "%s"', className, element.className);

  console.log('');
});

Future of Metaquery

Stripped out from GChat

Ben Schwarz
so the rewrite
I was working on
basically removes support for ie8
because … there is no media queries

Casey Leask
Is that the 'the-big-rewrite' branch?

Ben Schwarz
yeah, but I might not have pushed some stuff

Casey Leask
yeah probably, github says it was last updated four months ago

Ben Schwarz
looks like the local changes are just in test stuff
so I dropped ie8 support
which means we can use domContentLoaded etc
ahead of here, I was thinking about making the image stuff an optional plugin
which in this unsaved file
you’ll see is about 20 lines long
http://0.germanforblack.com.s3.amazonaws.com/untitled__metaquery_20131030_105817.jpg
(yes, I really wrote that 4 months ago, and never saved it)

Ben Schwarz
It was pseudo code…
I’m sure its fairly close to working anyway
otherwise, I want to make a mini site for it
and get some more adoption there
because its a rad library

Casey Leask
I like it :-)
If you make it into a plugin, I can basically do the same changes I did on the old source to it so you can apply multiple breakpoints.
For me, the retina/width combo is pretty useful.

Ben Schwarz
yeah, I can see that being useful
ideally, the ‘templating’ stuff
used in image

Casey Leask
I'd like to combine them somehow, so mobile+retina = small-tablet etc, but I'm not sure that first version stuff

Ben Schwarz
should work on any element with a data-attr-template=“/images/[breakpoint].jpg” data-att=“src”
and perhaps even pull other metadata, like the media query…
not sure that’d be useful
but basically, I thought
<meta name=“breakpoint” media=“@query here” data-var-name=“value”>
that data-var-name could be also shuffled through as meta data
… with me>
… thats all I have, anyway

Casey Leask
So switching the attr 'content' for 'data-var-name'?

Ben Schwarz
hum, more directly
to have variables
<img src=“small.jpg” data-mq-template=“[varname].jpg”>
… or something like that anyway
its kind of hard to believe I wrote the initial stuff 4 months ago

Casey Leask
ah, I think I get you.
Could it be used like this?
<img src="small.jpg" data-mq-template="[width]-[dpi].jpg">

Ben Schwarz
yeah
thats kind of where I was going
wouldn’t increase the code much
but it would mean you could use image processing server side etc
or… whatever

Casey Leask
yeah.

Ben Schwarz
so thats where I was hoping to go

Casey Leask
fair enough! I've made a gist of that so I can take a look at it later. https://gist.github.com/CaseyLeask/7225174

Ben Schwarz
Glen really likes putting mq into the head of the doc <script>the code here, literally</script>
after the meta tags
because its so small and fast that way
I haven’t messed with that much
but you could also use <script async src=“pth/to/mq”>

Casey Leask
yeah, setting it async in the head was what i was going to do
since the project I'm working on is in a CMS, the plan is to use the image stuff for CMS images and use the breakpoint classnames for the images defined in the css

Ben Schwarz
sounds fair

Casey Leask
that way I can sidestep the initial load of the mobile image in cases where I don't need it

Ben Schwarz
also, you can use <img> (without a src)
or even use breakpoint matchers to ajax in more content
perhaps

Casey Leask
<img data-mq-src="[breakpoint].jpg">
<noscript><img src="mobile"></noscript>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.