Modernizr.prefixed(str,obj) to use Function.prototype.bind? #478

Closed
paulirish opened this Issue Jan 29, 2012 · 14 comments

Comments

Projects
None yet
3 participants
@paulirish
Member

paulirish commented Jan 29, 2012

or just

 return function(){ return x.apply(navigator, arguments); }

The reason why

Some recent examples from user tests..

window[ Modernizr.prefixed('requestAnimationFrame', window) ]
battery = nav[ Modernizr.prefixed('battery', nav) ];
window[ Modernizr.prefixed('StorageInfo', window) ];

The double window/nav references kinda suck.. wondering if we just automatically bind the scope of it so we can pass back a function.

Hmmm

But would we only do this if the resulting thing is a function? And still just pass back a string otherwise?

how about Modernizr.prefixed('performance', window); .. pass back object or string?

Also to consider..

HTMLElement.prototype.webkitMatchesSelector
// so
Modernizr.prefixed('webkitMatchesSelector', HTMLElement.prototype); // return value ??

so we should make a call..what do we return?

  1. always return a string? (current behavior)
  2. return bound function in the case of functions?
  3. return actual reference for all cases (matchesSelector example disqualifies this)
  4. require a 3rd param to use as the binding scope (see comment below)

@ghost ghost assigned ryanseddon Jan 29, 2012

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jan 29, 2012

Member

a possibility for scoping for the last example from alex... use a 3rd param.

var myelem = evt.target;
var ms = Modernizr.prefixed( 'matchesSelector', HTMLElement.prototype, myelem)
if (ms('.item')) ...
Member

paulirish commented Jan 29, 2012

a possibility for scoping for the last example from alex... use a 3rd param.

var myelem = evt.target;
var ms = Modernizr.prefixed( 'matchesSelector', HTMLElement.prototype, myelem)
if (ms('.item')) ...
@ryanseddon

This comment has been minimized.

Show comment
Hide comment
@ryanseddon

ryanseddon Jan 29, 2012

Member

That is a tough one. I would argue that this about detecting and not creating a helper method, however being both would be advantageous. I could play around with a 3rd arg and see how it feels?

Member

ryanseddon commented Jan 29, 2012

That is a tough one. I would argue that this about detecting and not creating a helper method, however being both would be advantageous. I could play around with a 3rd arg and see how it feels?

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jan 30, 2012

Member

I think i wanna do the autobind.

questions:

  1. bind to 2nd arg automatically if its typeof .. == 'function' (and no 3rd arg) ?
  2. require 3rd arg to opt into binding

.

  • what to do about objects (like window.msPerformance) and props? return references and values or string as we do it today..
Member

paulirish commented Jan 30, 2012

I think i wanna do the autobind.

questions:

  1. bind to 2nd arg automatically if its typeof .. == 'function' (and no 3rd arg) ?
  2. require 3rd arg to opt into binding

.

  • what to do about objects (like window.msPerformance) and props? return references and values or string as we do it today..
@SlexAxton

This comment has been minimized.

Show comment
Hide comment
@SlexAxton

SlexAxton Jan 30, 2012

Member

Sorry was mobile earlier and couldn't chime in. I think we can do this pretty easily with something like the following:

Normal Case

// Since 'requestAnimationFrame' exists on window as a function, 
// return the function bound to window
Modernizr.prefixed('requestAnimationFrame', window)

Override to string:

// Passing false as a third param disables the possibility 
// of a function return, always a string
Modernizr.prefixed('requestAnimationFrame', window, false) // window.webkitRequestAnimationFrame

Override to other binding:

// force a binding by passing the bound object.
// This could be avoided by just passing in the element as the second param (as long as we look into prototypes).
Modernizr.prefixed( 'matchesSelector', HTMLElement.prototype, document.getElementById('theid'))

Downsides I can see:

  • Performance: it will be faster to get a string back once, and use it on all elements.
  • The case of no function being found returns a string, but when it's found it returns a function, so you'd have to test the output. This could be a bad thing.
Member

SlexAxton commented Jan 30, 2012

Sorry was mobile earlier and couldn't chime in. I think we can do this pretty easily with something like the following:

Normal Case

// Since 'requestAnimationFrame' exists on window as a function, 
// return the function bound to window
Modernizr.prefixed('requestAnimationFrame', window)

Override to string:

// Passing false as a third param disables the possibility 
// of a function return, always a string
Modernizr.prefixed('requestAnimationFrame', window, false) // window.webkitRequestAnimationFrame

Override to other binding:

// force a binding by passing the bound object.
// This could be avoided by just passing in the element as the second param (as long as we look into prototypes).
Modernizr.prefixed( 'matchesSelector', HTMLElement.prototype, document.getElementById('theid'))

Downsides I can see:

  • Performance: it will be faster to get a string back once, and use it on all elements.
  • The case of no function being found returns a string, but when it's found it returns a function, so you'd have to test the output. This could be a bad thing.
@SlexAxton

This comment has been minimized.

Show comment
Hide comment
@SlexAxton

SlexAxton Jan 30, 2012

Member

Perhaps that means you should have to opt in for it. Always returns a string, unless you pass a third param.

pass true for us to auto use the 2nd arg, and any other object to bind to that instead.

Further if you expect a function back, the result should be falsey if it doesn't exist (instead of the raw string like in the string version of the function). A noop function could potentially work, but I haven't thought into that much.

Member

SlexAxton commented Jan 30, 2012

Perhaps that means you should have to opt in for it. Always returns a string, unless you pass a third param.

pass true for us to auto use the 2nd arg, and any other object to bind to that instead.

Further if you expect a function back, the result should be falsey if it doesn't exist (instead of the raw string like in the string version of the function). A noop function could potentially work, but I haven't thought into that much.

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jan 30, 2012

Member

It should be falsy return if the test fails.. which would allow for a nice rAF polyfill

window.rAF = Modernizr.prefixed('requestAnimationFrame', window) || function(){ ... 

The rest sounds good to me. I would prefer to return the function / object / prop value when possible instead of its name.

Trying to think of any prefixed property names that are just string values or numbers...

Member

paulirish commented Jan 30, 2012

It should be falsy return if the test fails.. which would allow for a nice rAF polyfill

window.rAF = Modernizr.prefixed('requestAnimationFrame', window) || function(){ ... 

The rest sounds good to me. I would prefer to return the function / object / prop value when possible instead of its name.

Trying to think of any prefixed property names that are just string values or numbers...

@ryanseddon

This comment has been minimized.

Show comment
Hide comment
@ryanseddon

ryanseddon Jan 30, 2012

Member

Check out some early code in the prefixed branch 16a056d

I just grabbed the Function.prototype.bind shim from es5-shim

window.rAF = Modernizr.prefixed('requestAnimationFrame', window) || function(){ ...

This will now work as expected either returning a reference to rAF of false and falling back to function

// Passing in the 3rd arg will bind to that
// e.g. ms("div") -> true
var ms = Modernizr.prefixed("matchesSelector", HTMLElement.prototype, document.querySelector("div"))

So the three possible results that this change can get:

  • false
  • reference to object
  • reference to object with binding
Member

ryanseddon commented Jan 30, 2012

Check out some early code in the prefixed branch 16a056d

I just grabbed the Function.prototype.bind shim from es5-shim

window.rAF = Modernizr.prefixed('requestAnimationFrame', window) || function(){ ...

This will now work as expected either returning a reference to rAF of false and falling back to function

// Passing in the 3rd arg will bind to that
// e.g. ms("div") -> true
var ms = Modernizr.prefixed("matchesSelector", HTMLElement.prototype, document.querySelector("div"))

So the three possible results that this change can get:

  • false
  • reference to object
  • reference to object with binding
@ryanseddon

This comment has been minimized.

Show comment
Hide comment
@ryanseddon

ryanseddon Jan 30, 2012

Member

Also it doesn't do any hand holding so if someone tries to pass in something silly for the 3rd arg it will throw.

Member

ryanseddon commented Jan 30, 2012

Also it doesn't do any hand holding so if someone tries to pass in something silly for the 3rd arg it will throw.

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Feb 5, 2012

Member

ryan you got a patch for this alls et?

Member

paulirish commented Feb 5, 2012

ryan you got a patch for this alls et?

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Feb 5, 2012

Member

I just merged in Ryan's branch. Considering this ticket closed code-wise but we'll have to add docs.

Member

paulirish commented Feb 5, 2012

I just merged in Ryan's branch. Considering this ticket closed code-wise but we'll have to add docs.

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Feb 6, 2012

Member

Okay. code and tests updated. lots of tests!

i think this is basically what @SlexAxton proposed here
#478 (comment)

I have some extra checks so we're not calling .bind() on a non-function.
i think the tests explain the API the best, but

  • if prefixed(str,obj) identifies a fn then its auto-bind()'d to obj
  • if the third arg exists and is truthy, then the fn is bind()'d to it.
  • if the third arg is false, then its always returns the stringed name of the prop/method
  • if prefixed(str,obj) finds something that is a non-function, it just returns that.
Member

paulirish commented Feb 6, 2012

Okay. code and tests updated. lots of tests!

i think this is basically what @SlexAxton proposed here
#478 (comment)

I have some extra checks so we're not calling .bind() on a non-function.
i think the tests explain the API the best, but

  • if prefixed(str,obj) identifies a fn then its auto-bind()'d to obj
  • if the third arg exists and is truthy, then the fn is bind()'d to it.
  • if the third arg is false, then its always returns the stringed name of the prop/method
  • if prefixed(str,obj) finds something that is a non-function, it just returns that.
@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Feb 6, 2012

Member

Done except for docs.

Member

paulirish commented Feb 6, 2012

Done except for docs.

@ryanseddon

This comment has been minimized.

Show comment
Hide comment
@ryanseddon

ryanseddon Feb 6, 2012

Member

I approve this commit, good stuff!

Member

ryanseddon commented Feb 6, 2012

I approve this commit, good stuff!

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Feb 7, 2012

Member

docs added.

Member

paulirish commented Feb 7, 2012

docs added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment