why does angularjs and jquery give different results for innerWidth - javascript

I am working on my first responsive site, using 3 testing devices:
htc desire 500 dual,
iphone6, and
my windows laptop.
And I am testing with various 'languages':
css media queries
jquery
angular
When testing, angularjs gives strange results in the HTC:
#media screen and (max-width : 320px)
{ some_selector{ /* css that IS triggered */ }
}
jQuery(document).on( 'ready ',
function()
{ alert( jQuery( window ).innerWidth()) ;
}
) ; // RETURN: 320
my_app.controller( "my_controller" ,
function( $scope , $window )
{ alert( $window.innerWidth ) ;
}
) ; // RETURN: sometimes 980, sometimes 320, sometimes 192
EDIT
based on Jonathan's answer, I realize that this is just the same old problem of different browsers handling different window properties differently, especially during the document loading phase ... (which is basically the reason why everybody adopted jQuery, especially jQuery(document).ready(...) handler)
The call to angular's my_app.controller(...) is occurring before jquery's document ready phase, when the DOM is still going through initial setup phases (as you can see by the example below);
And the explanation of why HTC is different from iphone is almost certainly due to the fact that I am using firefox in the htc, and safari in the iphone ...
my_app
.controller( "my_controller" ,
function( $scope , $window )
{ alert( $window.innerWidth + "," + window.innerWidth + "," + window.document.documentElement.clientWidth ) ;
// returns 154,154,980
alert( $window.innerWidth + "," + window.innerWidth + "," + window.document.documentElement.clientWidth ) ;
// returns 320,320,320
}
) ;

While angular has a built in version of jQuery, it's called jqLite, at it doesn't have all the weight of jQuery. The short versions is that angular's $window is, by default, just the plain old Window object and its innerWidth property (see $WindowProvider).
jQuery is looking at different properties to determine the width. It does this to try to normalize values between browsers. If we are to examine jQuery's code (using v1.8.3 and up), we can conclude it is using window.document.documentElement.clientWidth instead.
If you want to learn more about the difference between these two, here is a StackOverflow question asking just that. Also, you could just switch out $window.innerWidth in angular for $window.document.documentElement.clientWidth if you prefer how jQuery calculates it.

Related

Can I put if condition in javascript to detect any running script

I want to develop chrome extension to put a check on the script say this website runs http://whatsmyscreenresolution.com/
e.g.
if (his_script==my_script)
then
block it or return "123".
I want to do something like this.Is it possible or can I even block websites to detect my screen resolution, font, etc other than disabling javascript at my end?
can I even block websites to detect my screen resolution
You could define a new window.screen object
(function (screen) {
function clone(e) {
var o = {}, k;
for (k in e) o[k] = e[k];
return o;
}
Object.defineProperty(window, 'screen', {get: function () {
var o = clone(screen);
o.availHeight = o.height = Math.random() * (o.height - 600) + 600;
o.availWidth = o.width = Math.random() * (o.width - 600) + 600;
return o;
}});
}(window.screen));
After this, trying to access screen or window.screen will give you randomised (but not entirely unreasonable for styling purposes) values
DEMO
Take a look at the chrome.webRequest api: https://developer.chrome.com/extensions/webRequest
Theoretically, you could do this with the onBeforeRequest listener.
It doesn't think it's possible. Tried setting window.screen and creating a var screen but no matter what is written to screen.width and screen.height it always returns the correct resolution. It doesn't seem spoofable at least from a javascript console. You might try a hidden frame with the desired screen resolution for privacy and when the page is loaded adjust the resolution to actual browser resolution and display the frame.

testing keydown events in Jasmine with specific keyCode

I am writing tests for an AngularJS directive which fires events of a <textarea> when certain keys are pressed. It all works fine per my manual testing. I want to be good and have a full unit-test suite too, but I have run into a problem I can't solve on my own:
I want to send a specific keyCode in my triggerHandler() call in my test, but I can't find a way to specify the key that actually works. I am aware of a lot of questions and answers on the topic of building and sending events with specific data, but none of them work on my setup:
My setup
Karma test runner
PhantomJS browser running the tests (but also tried Firefox and Chrome without success)
I'm not using jQuery and I'm hoping there is a regular JS solution. There must be!
Test code
var event = document.createEvent("Events");
event.initEvent("keydown", true, true);
event.keyCode = 40; // in debugging the test in Firefox, the event object can be seen to have no "keyCode" property even after this step
textarea.triggerHandler(event); // my keydown handler does not fire
The strange thing is, I can type the first 3 lines into the console in Chrome and see that the event is being created with the keyCode property set to 40.
So it seems like it should work.
Also, when I call the last line like this textarea.triggerHandler("keydown"); it works and the event handler is triggered. However, there is no keyCode to work with, so it is pointless.
I suspect it may be something to do with the nature of the test running against a DOM that is different to a regular page running in the browser. But I can't figure it out!
I've used the following solution to test it and having it working in Chrome, FF, PhantomJS and IE9+ based on this SO answer.
It doesn't work in Safari - tried millions of other solution without any success...
function jsKeydown(code){
var oEvent = document.createEvent('KeyboardEvent');
// Chromium Hack: filter this otherwise Safari will complain
if( navigator.userAgent.toLowerCase().indexOf('chrome') > -1 ){
Object.defineProperty(oEvent, 'keyCode', {
get : function() {
return this.keyCodeVal;
}
});
Object.defineProperty(oEvent, 'which', {
get : function() {
return this.keyCodeVal;
}
});
}
if (oEvent.initKeyboardEvent) {
oEvent.initKeyboardEvent("keydown", true, true, document.defaultView, false, false, false, false, code, code);
} else {
oEvent.initKeyEvent("keydown", true, true, document.defaultView, false, false, false, false, code, 0);
}
oEvent.keyCodeVal = code;
if (oEvent.keyCode !== code) {
console.log("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ") -> "+ code);
}
document.getElementById("idToUseHere").dispatchEvent(oEvent);
}
// press DEL key
jsKeydown(46);
Hope it helps
Update
Today I've found and tested this solution which is offers a much wider coverage of browsers (enabling the legacy support):
https://gist.github.com/termi/4654819
All the credit goes to the author of this GIST.
The code does support Safari, PhantomJS and IE9 - tested for the first 2.
Adding to #MarcoL answer, I'd like to point out for future readers who might stumble on this question, that the methods initKeyboardEvent and initKeyEvent are deprecated methods, and should no longer be used. See here and here.
Instead as the MDN docs suggested, events should be created via their respective constructor.

Detect device type with UI Automation

I am using ui-screen-shooter, which makes use of the UI Automation JavaScript API to take screenshots of apps. My app has a slightly different structure on iPad and iPhone, so I need to detect the device type in my shoot_the_screen.js script and run different code. I would like something equivalent to [[UIDevice currentDevice] userInterfaceIdiom] that I can use in JavaScript. Here is the best I have come up with. It works, but do you know of a cleaner, less device-dependent way to get the same information?
var target = UIATarget.localTarget();
var width = target.rect().size.width;
if (width == 1024 || width == 768)
{
// iPad
}
else
{
// iPhone
}
You can call model() on the target to get the information you need. That's exactly what I'm doing in the ui-screen-shooter itself.
var modelName = UIATarget.localTarget().model();
// This prints "iPhone" or "iPad" for device. "iPhone Simulator" and "iPad Simulator" for sim.
UIALogger.logMessage(modelName);

How to generically test if mediaqueries are supported?

May sound odd, but I'm looking for a simple way to test if any mediaqueries are supported on a browser.
I know there is respondjs, which comes with a window.matchMedia polyfill, but even using it, I still have to query specific queries like:
window.matchMedia("all and (min-width: 400px"));
which returns an obj.matches = true/false.
What I'm looking for is a generic way to test, "if mediaQueries are supported", yes or no.
I'm currently using:
window.matchMedia("screen and (orienation:landscape),
screen and (orientation:portrait)");
but I don't really like this way of testing.
I also tried:
window.matchMedia("all");
but this (of course) returns true in IE8 when using the matchMedia polyfill. Also, I can't test for matchMedia itself, because I would miss out on a lot of browsers that support media queries, but not window.matchMedia (caniuse).
Question:
Is there a nice and simple way to test for media query support? If possible, I would not want to use window.matchMedia at all to do that.
EDIT:
I also checked Modernizr, which also tests for a specific mediaQuery condition (mq):
testMediaQuery = function( mq ) {
var matchMedia = window.matchMedia || window.msMatchMedia;
if ( matchMedia ) {
return matchMedia(mq).matches;
}
var bool;
injectElementWithStyles('#media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) {
bool = (window.getComputedStyle ?
getComputedStyle(node, null) :
node.currentStyle)['position'] == 'absolute';
});
return bool;
},
So no generic test, if mediaQueries are supported.
U can choose this option in modernizr
http://modernizr.com/download/

Using mootools Fx to modify the zoom of a div

I have a case where I'd like to animate the zoom style of a div (and it's entire contents). Legibility is not an issue, as this will be to 'pop' the element into view. Now I can get just about every other style aspect animating with Fx, but I can't seem to get this working for the zoom.
This is in chrome (though obviously I want to get it browser agnostic as well).
Using the Elements tab in the Chrome dev tools I can manually set the zoom style on the html element and it behaves accordingly, so I knew it must be possible to modify in code. Using the answer on this question: zoom css/javascript
I can get the element to zoom in and out with code:
dialog.setStyle('WebkitTransform', 'scale(0.1)')
Now I could write a 'native' function to do this, but then I'd lose out on all the great animation options in Fx. Can anyone suggest an elegent way to achieve this with Fx?
yes, you need to hack some of the CSS parsers in the mootools core to enable FX to work with them.
check this fun example I did a while back for another SO problem: http://jsfiddle.net/dimitar/ZwMUH/ - click on any 2 icons to swap them and it will transition them via scale.
or this light box base class I wrote that also uses it: http://jsfiddle.net/dimitar/6creP/
at its basic, start by modding the parsers:
Element.Styles.MozTransform = "rotate(#deg) scale(#)";
Element.Styles.MsTransform = "rotate(#deg) scale(#)";
Element.Styles.OTransform = "rotate(#deg) scale(#)";
Element.Styles.WebkitTransform = "rotate(#deg) scale(#)";
Object.append(Fx.CSS.Parsers, {
TransformScale: {
parse: function(value) {
return ((value = value.match(/^scale\((([0-9]*\.)?[0-9]+)\)$/i))) ? parseFloat(value[1]) : false;
},
compute: function(from, to, delta) {
return Fx.compute(from, to, delta);
},
serve: function(value) {
return 'scale(' + value + ')';
}
}
});
also relevant, define public and scripting vers of all styles cross browser:
transforms: {
computed: ['transformProperty', 'WebkitTransform', 'MozTransform', 'OTransform', 'msTransform'],
raw: ['transform', '-webkit-transform', '-moz-transform', '-o-transform', 'msTransform']
};
detection method which will loop through the transforms defined above and return the first one that the element supports as the definitive property to work with in the future, or opacity as fallback if unavailable:
var testEl = new Element("div"),
self = this;
this.scaleTransform = this.options.transforms.computed.some(function(el, index) {
var test = el in testEl.style;
if (test) {
self.prop = self.options.transforms.raw[index];
}
return test;
});
if (!this.prop) {
this.prop = "opacity";
}
then this.prop will refer to the correct browser property, vendor prefixed or opacity as fallback for tweening/morphing whereas this.scaleTransform will be a boolean that points to the ability to scale - you can then check against that to see if its supported when you are creating the morph object.
The object itself would be like this:
var morphObj = {};
morphObj[this.prop] = ["scale(0)", "scale(1)"]; // call it dynamically
el.morph(morphObj);
other solutions are available such as this plugin http://mootools.net/forge/p/fx_tween_css3, there's also one by Theiry Bela I know of: https://github.com/tbela99/Fx.css
its also going to be natively available in mootools 2.0
have fun.

Categories