I'm trying to detect orientation changes on mobile devices and trigger a function once an orientation has been completed, but the code inside the function is continuously firing in Android and I'm not sure why. Here's my code:
var supportsOrientationChange = "onorientationchange" in window;
var orientationEvent = supportsOrientationChange ? "orientationchange" : "resize";
window.addEventListener(orientationEvent,
function() { alert ('orientation changed'); },
false
);
Does anyone know how to write this so that it only triggers once, after orientation change has been completed?
I used a work-around for this in my app. I added an event listener for orientationchange and then set a timeout so the orientationchange could occur and I could get a new width value that actually reflected the width after the orientation change.
var device_width = 0;
$(document).ready(function () {
var oc_timer;
$(window).bind('orientationchange', function () {
clearTimeout(oc_timer);
oc_timer = setTimeout(function () {
device_width = $(window).width();
}, 500);
});
});
I am not positive this will solve your continuously firing function problem but this is a solution I found to work.
I found a way to do this in case others are still having the same issue, check it out:
function onOrientationChange() {
if (typeof(this.orientation) === "undefined"){
if (window.orientation == -90 || window.orientation == 90) {
this.orientation = "landscape";
} else {
this.orientation = "portrait";
}
// alert ("in 1");
// you code here for change
}
if ((window.orientation == -90 || window.orientation == 90) && (this.orientation == "portrait")) {
this.orientation = "landscape";
// alert ("in 2");
// you code here for change
}
if ((window.orientation == 0 || window.orientation == 180) && (this.orientation == "landscape")) {
this.orientation = "portrait";
// alert ("in 3");
// you code here for change
}
}
Simple solution for less detailed applications
//bind orientation change event
$(window).bind("orientationchange", orientationChange);
//later,
function orientationChange(){
//whatever needs to happen when it changes.
alert("changed");
}
You could also use $.on() instead if you need to pass information.
In browsers that do not support it, the event does not fire. You could then write a custom detection script as a fallback to fire the event if the orientation changes on a device that does not support onorientationchange.
Related
I am trying to create a Navigation Bar that slides in and out when clicked on using JavaScript and Greensock. For some reason, the on click action is randomly not working when clicked on at different sizes but sometimes it works perfectly fine.
My code is below, you can find a live example of this navigation at: http://www.kramergraphicdesign.com/Maura_Website/
var resize = function(){
var viewportWidth = $(window).width();
var lastLiWith = $('#logo').width();
console.log(openOrShut + " this is the true false var");
if ($(window).width() >= 0 && $(window).width() <= 639 ) {
console.log("mobile");
$("#logo, #close, .arrow-right").click(function()
{
console.log("mobile-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:viewportWidth-lastLiWith});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
else if ($(window).width() >= 640 ) {
console.log("tablet");
$("#logo, #close, .arrow-right").click(function()
{
console.log("tablet-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:400});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
else if ($(window).width() >= 1025 && $(window).width() <= 10000 ) {
console.log("dekstop");
$("#logo, #close, .arrow-right").click(function()
{
console.log("desktop-click");
if(openOrShut === false)
{
TweenLite.to("#custom-nav",".5",{x:400});
openOrShut = true;
}
else{
TweenLite.to("#custom-nav",".5",{x:0});
openOrShut = false;
}
});
}
};
$(document).ready(resize);
$(window).resize(function(){
resize();
});
First of all, the resize event can occur an awful lot, especially during a drag to resize the window. This means two things:
Minimise the amount of work you do so it runs fast, or debounce the function (e.g. using Lodash) so it only runs after you stop receiving resize events for a short time.
More importantly, you are adding a new click handler every single time.
So the reason it "randomly" doesn't do anything is that whenever you click, you actually run your function to toggle the menu many, many times if you have previously resized the window at all. If that number of times happens to be even, then there is no net effect.
There are probably a number of ways to fix this, but here are two:
Attach a click handler once, but check the width inside the handler to determine how far to animate it to / how to respond differently to different sizes.
Unregister existing click events first (using jQuery's .off()) before re-adding them, so there is only ever the one handler registered. I recommend using an event namespace so you can deregister everything on the namespace at once.
Bonus observation: your condition for the tablet widths means the desktop code will never run, because there is no <= 1024 condition for the tablet block.
I use the following code to detect whether the device is a touch device or not:
var isTouchDevice = 'ontouchstart' in window || navigator.msMaxTouchPoints;
if(isTouchDevice)
{
$('body').addClass('yes-touch');
}
else
{
$('body').addClass('no-touch');
}
I use this to only show :hover states when it is NOT a touch device (as most touch devices interpret a tap as a hover).
.no-touch .element:hover {
color: red;
}
The problem is, one of our PCs in the office is an all-on-one touch screen PC, which means that when using a mouse the hover states don't occur.
Is there a way to work out whether a mouse is being used on a touch screen device? In other words, it should have the no-touch class applied when the mouse is being used and the yes-touch class applied when the touch screen is being used.
As of today, there is no foolproof ironclad way of doing it. The modernizr folks, pretty much the experts in feature detection, recently had this to say about it:
https://github.com/Modernizr/Modernizr/issues/869#issuecomment-57891034
The end result of all of this is that you cannot detect a mouse use in
a way that would conform to the level of reliability that Modernizr is
credited with. For our intents and purposes, it is a undetectable.
If you, future traveler, wish to attempt to detect a mouse user, then
the following is the best guide I can offer.
Don't. Seriously. Just because a user has a "mouse" doesn't mean that
they don't have multiple other forms of input. You should try really
hard to avoid making any kind of UI/UX decision that changes based
upon the idea of a mouse user being diametrically opposed to a
touchscreen user (or any other kind, for that matter). Make things
universal.
If you have to, and only care about IE 10 and 11, then IE's
PointerEvent would be worth checking out. Support is abysmal, outside
of those two (and presumably future IE versions).
You can attach a
listener for a 'hover' event on the body, and if it is true, then the
user probably has a mouse. The drawback with this approach include
touch events briefly firing hover events on tap/touch, so you could
get false positives.
sniff for mobile user agents. This is a bad idea,
and goes against the very core of Modernizr. Please don't do it.
So to me #1 pretty much sums it up. However, that answers your question but doesn't give you a solution. You mention "one of our PC's in the office..." Is this by chance an internal only application? I've occasionally run across situations where internal special use or one off pages may require some individual treatment for whatever reason (like one of our employees having a touch based AIO with a mouse attached). What I'll do then is append a ?hasmouse onto the end of the url and give the user that link to bookmark. Then inside javascript after your var isTouchDevice but before your if, insert this code to undo it:
if (location.search == '?hasmouse') {
isTouchDevice = false;
}
Again, thats sort of a no frills way for just internal use.
I have been using this for a while and it seems to work reliably. I wounder if it's worth it sometimes, but it does work.
The idea here is to capture actual touchdown events to trigger touch mode and use mousemove to trigger mouse mode. The problem is IE does not trigger touch events, but pointer events. The great thing about pointer events is you can check if it's mouse or touch!
The problem is all other browsers fire a fake mousemove just after a touch event. It's truly maddening!
You can see it work on this codepen
//First check if this is a touch device:
this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
// Some vars we'll need later
var lastTouch = 0
var lastCheck = 0
//Then set up our event listeners:
function initEvents() {
//handle touch/mouse devices detect mouse so that touch is toggled off
if (this.isTouch) {
$(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
e = e.originalEvent
//browser has pointer events
var pe = window.PointerEvent || window.MSPointerEvent
// handle ie pointer events (polyfill functions are at bottom of answer)
if (e.type == msPointerEvent('move')) {
var touchEvent = msPointerType(e) == 'touch'
if (touchEvent)
lastTouch = e.timeStamp;
if (!this.isTouch && touchEvent)
return setupTouch.call(this, true)
else if (this.isTouch && !touchEvent)
return setupTouch.call(this, false)
}
// Handle all other browser touch events
if (e.type == "touchstart") {
console.log('touchstart fired')
lastTouch = e.timeStamp;
if (!this.isTouch)
setupTouch.call(this, true);
}
// test mouse move and set up mouse mode if real
else if (!pe && e.type == "mousemove" && this.isTouch) {
if (realMouseDown.call(this, e)) {
setupTouch.call(this, false)
}
}
}.bind(this));
}
}
initEvents()
// Here is where we get clever. It turns out that the fake mousemove will fire in less than 500ms of the touch so we use that to detect fakes. Then of course do something special for IE:
function realMouseDown(e) {
var touchDif = e.timeStamp - lastTouch
var mouseDif = e.timeStamp - lastCheck
// false mouse event will get fired within 500ms of a touch (touchDif > 500)
// (required for all browsers false mouse after touch event)
var real = touchDif > 500
lastCheck = e.timeStamp;
console.log('real=', real, ' mDif ='+mouseDif, ' tDif ='+touchDif)
return real
}
// Now for some IE polyfill because they cant seem to make up their mind what to do.
// IE pointer event polyfill
function msPointerEvent(type) {
var n = ""
if (window.PointerEvent) // IE 11
n = 'pointer' + type
else if (window.MSPointerEvent) // IE 10
n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
return n
}
// IE pointer type polyfill
function msPointerType(e) {
var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
}
// And finally do what you need...
// make required changes for touch / mouse
var $output = $('#output')
function setupTouch(state) {
console.log('TouchMode=', state)
if (state)
this.isTouch = true
else
this.isTouch = false
$output.html('Touch mode changed to = '+state)
}
//First check if this is a touch device:
this.isTouch = 'ontouchstart' in window || (navigator.msMaxTouchPoints > 0);
// Some vars we'll need later
var lastTouch = 0
var lastCheck = 0
//Then set up our event listeners:
function initEvents() {
//handle touch/mouse devices detect mouse so that touch is toggled off
if (this.isTouch) {
$(document).on(" touchstart mousemove " + msPointerEvent('move'), function(e) {
e = e.originalEvent
//browser has pointer events
var pe = window.PointerEvent || window.MSPointerEvent
// handle ie pointer events (polyfill functions are at bottom of answer)
if (e.type == msPointerEvent('move')) {
var touchEvent = msPointerType(e) == 'touch'
if (touchEvent)
lastTouch = e.timeStamp;
if (!this.isTouch && touchEvent)
return setupTouch.call(this, true)
else if (this.isTouch && !touchEvent)
return setupTouch.call(this, false)
}
// Handle all other browser touch events
else if (e.type == "touchstart") {
console.log('touchstart fired')
lastTouch = e.timeStamp;
if (!this.isTouch)
setupTouch.call(this, true);
}
// test mouse move and set up mouse mode if real
else if (!pe && e.type == "mousemove" && this.isTouch) {
if (realMouseDown.call(this, e)) {
setupTouch.call(this, false)
}
}
}.bind(this));
}
}
initEvents()
// Here is where we get clever. It turns out that the fake mousemove will fire in less than 500ms of the touch so we use that to detect fakes:
function realMouseDown(e) {
var touchDif = e.timeStamp - lastTouch
var mouseDif = e.timeStamp - lastCheck
// false mouse event will get fired within 500ms of a touch (touchDif > 500)
// (required for all browsers false mouse after touch event)
var real = touchDif > 500
lastCheck = e.timeStamp;
console.log('real=', real, ' mDif =' + mouseDif, ' tDif =' + touchDif)
return real
}
// IE pointer event polyfill
function msPointerEvent(type) {
var n = ""
if (window.PointerEvent) // IE 11
n = 'pointer' + type
else if (window.MSPointerEvent) // IE 10
n = 'MSPointer' + type[0].toUpperCase() + type.substr(1);
return n
}
// IE pointer type polyfill
function msPointerType(e) {
var pt = ['zero', 'one', 'touch', 'pen', 'mouse']
return typeof e.pointerType == 'string' ? e.pointerType : pt[e.pointerType]
}
// make required changes for touch / mouse
var $output = $('#output')
function setupTouch(state) {
console.log('TouchMode=', state)
if (state) {
this.isTouch = true
$output.addClass('is-touch')
} else {
this.isTouch = false
$output.removeClass('is-touch')
}
$output.html('Touch mode changed to = ' + state)
}
body {
pointer-evetns: none;
}
#output.is-touch {
background-color: blue;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output">
Touch or movethe mose on the result window to change the TouchMode state.
</div>
You can check for type of Pointer Event, that attached to Your object.
Please see example for hover below:
$('.element').on('pointerenter', function (e) {
if (e.pointerType == 'mouse') {
$(this).addClass('hover');
}
}).on('pointerleave', function (e) {
if (e.pointerType == 'mouse') {
$(this).removeClass('hover');
}
});
And use your css:
.element.hover {
color: red;
}
I am trying to run some code when the browser back button is clicked.
How can i found out browser's back button with out changing the browser history?
I tried the code below.
I got an exception in the else block saying: "event is not defined".
window.onunload = HandleBackFunctionality();
function HandleBackFunctionality()
{
if(window.event)
{
if(window.event.clientX < 40 && window.event.clientY < 0)
{
alert("Browser back button is clicked…");
} else {
alert("Browser refresh button is clicked…");
}
} else {
if(event.currentTarget.performance.navigation.type == 1)
{
alert("Browser refresh button is clicked…");
}
if(event.currentTarget.performance.navigation.type == 2)
{
alert("Browser back button is clicked…");
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
use
$(window).on("navigate", function (event, data) {
var direction = data.state.direction;
if (direction == 'back') {
// do something
}
if (direction == 'forward') {
// do something else
}
});
Okay. Besides the fact that you should not initially trigger the event and to .unload = FunctionName and not .unload=FunctionName() and that you need to pass the event-argument I checked the code in the browser.
currentTarget is empty - this totally makes sense as there is no event-target like onclick but it is just the site reloading/unloading.
Please debug the code by yourself by using this and fit it to your needs:
window.onunload = HandleBackFunctionality;
function HandleBackFunctionality(event)
{
console.log(event, window.event);
}
You will see that currentTarget is not set (while event is).
This is the only solution that works for me with IOS safari.
<script>
window.addEventListener( "pageshow", function ( event ) {
var pagehistory = event.persisted ||
( typeof window.performance != "undefined" &&
window.performance.navigation.type === 2 );
if ( pagehistory ) {
// back button event - Do whatever.
}
});
</script>
I have 3 buttons with hover states which makes a little tooltip appear to describe the button. They work fine but on touchs screen they do not disappear after the user clicks on the button.
So I've tried a few js scripts for checking if a device is a touch device or not. They almost work but they also when I test on IE11 it also gets detected as a touch device. Chrome & Firefox do not get mistaken as a touch device.
Any sugestions?
Her is what I've tried
/*****************************
TOUCH DEVICES HOVER FIX START
****************************/
// http://stackoverflow.com/a/4819886/1814446
function isTouchDevice() {
return 'ontouchstart' in window // works on most browsers
|| 'onmsgesturechange' in window; // works on ie10
};
// http://www.stucox.com/blog/you-cant-detect-a-touchscreen/#poke-it
var hasTouch;
window.addEventListener('touchstart', function setHasTouch () {
hasTouch = true;
// Remove event listener once fired, otherwise it'll kill scrolling
// performance
window.removeEventListener('touchstart', setHasTouch);
}, false);
// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touchevents.js
define(['Modernizr', 'prefixes', 'testStyles'], function( Modernizr, prefixes, testStyles ) {
// Chrome (desktop) used to lie about its support on this, but that has since been rectified: http://crbug.com/36415
Modernizr.addTest('touchevents', function() {
var bool;
if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
bool = true;
} else {
var query = ['#media (',prefixes.join('touch-enabled),('),'heartz',')','{#modernizr{top:9px;position:absolute}}'].join('');
testStyles(query, function( node ) {
bool = node.offsetTop === 9;
});
}
return bool;
});
});
if(bool===true) {
console.log('Touch Device'); //your logic for touch device
jQ( "#btn-1, #btn-2, #btn-3" ).click(function() {
jQ("#btn-1 .tooltip").css('opacity', '0');
jQ("#btn-2 .tooltip").css('opacity', '0');
jQ("#btn-3 .tooltip").css('opacity', '0');
});
}
else {
//your logic for non touch device
}
For IE10+ you can utilize "window.navigator.msMaxTouchPoints"
example code
function isIETouch ()
{
return window.navigator.msMaxTouchPoints == undefined ? false : window.navigator.msMaxTouchPoints;
}
Good day all.
I'm having some problems with hoverintent.js a jquery plugin that handle the mouseOver events in a different way than normal.
Due to some complications, I can't modifiy anything but the js of this plugin, but I need to make it compliant with touch events and not only with mouseOver and mouseLeave.
after some debugs, I have managed to recognize this part of the code to be the one to modify:
var handleHover = function(e) {
// next three lines copied from jQuery.hover, ignore children onMouseOver/onMouseOut
var p = (e.type == "mouseover" ? e.fromElement : e.toElement) || e.relatedTarget;
while ( p && p != this ) { try { p = p.parentNode; } catch(e) { p = this; } }
if ( p == this ) { return false; }
// copy objects to be passed into t (required for event object to be passed in IE)
var ev = jQuery.extend({},e);
var ob = this;
// cancel hoverIntent timer if it exists
if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); }
// else e.type == "onmouseover"
if (e.type == "mouseover") {
// set "previous" X and Y position based on initial entry point
pX = ev.pageX; pY = ev.pageY;
// update "current" X and Y position based on mousemove
$(ob).bind("mousemove",track);
// start polling interval (self-calling timeout) to compare mouse coordinates over time
if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );}
// else e.type == "onmouseout"
} else {
// unbind expensive mousemove event
$(ob).unbind("mousemove",track);
// if hoverIntent state is true, then call the mouseOut function after the specified delay
if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );}
}
}
};
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
what I've done so far is to make the function working different with mobiles:
var handleHover = function(e) {
isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
if(isMobile){
console.log("Ismobile");
}else{
... Same code as before here ...
}
// bind the function to the two event listeners
return this.mouseover(handleHover).mouseout(handleHover);
and now i'm struck. I would like it to "change" its behavior to handle the touch, and not the mouse over event, so on mobiles I will need to touch the element, instead to hovering on it. May someone give me an help? Am I on the right way? Is it the right way to think of it?
unluckily I have only the possibility to change this file and some few more.
Recently i bumped into several problems with changing hoverIntent.js, and ended up in writing my own plugin: hoverDelay.js (much simpler, and less code). see if you can use it, and modify it to your own needs (and maybe contribute the mobile code to it :-)