I want to detect if users swipe from the edge or not before I open the panel with $("#panel").panel("open");. Here is the code
$("#main").on("swiperight",function(event){
var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
coords = [data.pageX, data.pageY];
console.log(coords);
});
However the coords didn't return anything because I got error:
Uncaught TypeError: Cannot read property 'touches' of undefined
So is there a way to get coordinate when swipe happens?
Or is there an easy way to detect if the staring position is from
the left edge instead?
Thanks.
Try below in jqm 1.4+, worked for me, adjust the edge trim value accordingly, 50 was good for me
$( "div.ui-page" ).on( "swiperight", function( e ) {
if ( e.swipestart.coords[0] <50) {
// your logic
}
});
this is for x coordinate similarly you can get y from coords[1]
this will work for any screen size:
$("#main").on("swiperight",function( event ){
// take 15% of screen good for diffrent screen size
var window_width_15p = $( window ).width() * 0.15;
// check if the swipe right is from 15% of screen (coords[0] means X)
if ( event.swipestart.coords[0] < window_width_15p) {
// open your panel
$("#panel").panel("open");
}
});
You can do something like this.
$('#main').on('touchstart', function(e) {
var xPos = e.originalEvent.touches[0].pageX;
if(xPos == 0) { //Keep in mind the margin of failure from 0, when user touches.
//Handle edge swipe
}
});
Related
I'm working on a javascript draggable interaction that has to work with both mouse and touch input and does not have any dependencies. So far it works fine on desktops and mobiles.
Except Firefox for Android shows the following behaviour:
page is not scrolled: fine
page is scrolled vertically: element can only be dragged horizontally
page is scrolled horizontally: element can only be dragged vertically
page is scrolled both vertically and horizontally: element can't be
dragged at all
scroll page back to the very top and left: element can be dragged as
expected again
The code:
var evtStart, evtMove, evtEnd;
if ('ontouchend' in window) {
evtStart = 'touchstart';
evtMove = 'touchmove';
evtEnd = 'touchend';
} else {
evtStart = 'mousedown';
evtMove = 'mousemove';
evtEnd = 'mouseup';
}
// BASIC DRAGGABLE INTERACTION
// No further configuration, just drags ....
var panel = document.querySelector('.testpanel');
panel.addEventListener(evtStart, function(e) {
e.preventDefault();
var styles = window.getComputedStyle(panel),
left = parseFloat(styles.left), // css left on mousedown
top = parseFloat(styles.top), // css top on mousedown
psx = e.pageX || e.touches[0].pageX, // pointer x on mousedown
psy = e.pageY || e.touches[0].pageY; // pointer y on mousedown
// function actually draging the elmt
var drag = function (e) {
e.preventDefault();
var pmx = e.pageX || e.touches[0].pageX, // current pointer x while pointer moves
pmy = e.pageY || e.touches[0].pageY; // current pointer y while pointer moves
panel.style.left = left + (pmx - psx) + 'px'; // set new css left of elmt
panel.style.top = top + (pmy - psy) + 'px'; // set new css top of elmt
};
panel.addEventListener(evtMove, drag);
panel.addEventListener(evtEnd, function () {
panel.removeEventListener(evtMove, drag);
});
});
Demo page
Again, it works fine on desktop and mobiles except FF for Android.
Why does it not work on FF for Android? Is it something in my code or is it a bug in FF? So far I could not find anything helpful.
I would greatly appreciate any help.
Finally I found it myself:
Just swap pageX/pageY with clientX/clientY and it works in FF for Android as well.
I guess the implementation in FF for Android is somewhat different, pageX/pageY are not finalized standards yet according to MDN.
I have an element with two markers over it. The first marker sets minimum value of something, and the second, maximum. That said, I want to prevent the "maximum" one be lower than "minimum" one. I'm using jQuery UI's draggable as a wrapper around HTML5 drag and drop, and here's my code:
component = {}; // actually, it's a real component but let's pretend it's simply a plain-object.
component.containerWidth = $('.marker').parent().width();
$('.marker').each(() => {
$(this).draggable({
containment: 'parent',
axis: 'y',
create: (event, ui) => {
if ($(this).hasClass('min')) {
component.minMarker = $(this).position().top;
} else if ($(this).hasClass('max')) {
component.minMarker = $(this).position().top;
}
},
drag: (event, ui) => {
const isDraggingMin = $(this).hasClass('min');
const isDraggingMax = $(this).hasClass('max');
const isMinLower = ui.position.top + $(this).height() <= component.maxMarker;
const isMaxHigher = ui.position.top - $(this).height() >= component.minMarker;
if (isDraggingMin && isMinLower) {
// set actual markers' positions
// do something
} else if (isDraggingMax && isMaxHigher) {
// set actual markers' positions
// do something
} else {
event.preventDefault();
}
}
});
});
The problem is that, when preventDefault gets invoked, the dragging doesn't work anymore until I stop dragging and start it over. It means, if I drag a "minimum" marker as close to "maximum" marker as event.preventDefault() gets fired, then no matter how and where I drag, the marker stays. If I release it and then start dragging again, it drags until it gets into the same conditions.
Simply setting css properties via $(...).css({left: ...}) does not work, and perhaps it shouldn't.
So how do I solve it then? How can I allow dragging without having user to release and grab a marker once in a while when they occasionally approach another marker?
The codepen: http://codepen.io/rishatmuhametshin/pen/WrvoKd
I have some other thing in mind for you,, see this code and I hope that can fit in your need,, or at least can help you to see this through another lens (I'm wearing glasses btw :)
http://codepen.io/mkdizajn/pen/bEdgLQ?editors=001
in place for that preventDefault call I used this peace of code:
$(event.target).css('top', $(event.target).data('startpos') )
in relation to:
$(event.target).data('startpos', ui.position.top);
My idea was to track start/stop evt and to do some stuff there,, you can see and read my comments on codepen,, hope that can be of some help,
cheers, k
You need to change your if statement to this, and get rid of the preventDefault:
if ( draggingMin ) {
if ( isMinLower ) {
component.minPosition = ui.position.top;
} else {
ui.position.top = component.minPosition;
}
} else if (draggingMax) {
if( isMaxHigher ){
component.maxPosition = ui.position.top;
} else {
ui.position.top = component.minPosition;
}
}
On a Windows phone, in IE users can go back and forward by swiping on the screen if the swipe is coming from the edge. This OS level functionality is hampering my webpage's UX.
Is there any js or css which can disable that? Some hack would also do.
A snapshot from windowsphone's website:
Here is the link to the reference page: http://www.windowsphone.com/en-in/how-to/wp8/basics/gestures-swipe-pan-and-stretch
Please note that I still need touchaction enabled for horizontal scrolling.
You Should Try this solution in two way :
1) CSS only fix for Chrome/Firefox
html, body {
overscroll-behavior-x: none;
}
2) JavaScript fix for Safari
if (window.safari) {
history.pushState(null, null, location.href);
window.onpopstate = function(event) {
history.go(1);
};
}
Over time, Safari will implement overscroll-behavior-x and we'll be able to remove the JS hack
Possible duplicate of iOS 7 - is there a way to disable the swipe back and forward functionality in Safari?
Copy and paste this JavaScript:
var xPos = null;
var yPos = null;
window.addEventListener( "touchmove", function ( event ) {
var touch = event.originalEvent.touches[ 0 ];
oldX = xPos;
oldY = yPos;
xPos = touch.pageX;
yPos = touch.pageY;
if ( oldX == null && oldY == null ) {
return false;
}
else {
if ( Math.abs( oldX-xPos ) > Math.abs( oldY-yPos ) ) {
event.preventDefault();
return false;
}
}
} );
If you want it minified, copy and paste this:
var xPos=null;var yPos=null;window.addEventListener("touchmove",function(event){var touch=event.originalEvent.touches[0];oldX=xPos;oldY=yPos;xPos=touch.pageX;yPos=touch.pageY;if(oldX==null && oldY==null){return false;}else{if(Math.abs(oldX-xPos)>Math.abs(oldY-yPos)){event.preventDefault();return false;}}});
How about preventing the default action of the swipe event. Somewhere in your document.ready add (note I've included the document.ready in this example, only the function needs to be added):
$(document).ready(function(){
$(window).on('touchmove',function(e){e.preventDefault();});
});
In this case I believe the event is called 'touchmove'you may need to extend this to also ignore default behavior of touchstart/touchend but I'm not 100% sure.
Check out this Codepen by David Hill.
var elem = document.getElementById("container"); //area we don't want touch drag
var defaultPrevent=function(e){e.preventDefault();}
elem.addEventListener("touchstart", defaultPrevent);
elem.addEventListener("touchmove" , defaultPrevent);
I actually think this is a cool way to build objects too.
This is also a problem for Mac users who have configured swipe-left to navigate backwards. You can't disable this setting, but you can prompt the user to confirm that they intended to navigate away https://developer.mozilla.org/en-US/docs/Web/Events/beforeunload.
*{
-webkit-touch-callout: none;
}
The result is to completely deactivate any touch events
I have this event:
$(window).scroll(function(e){
console.log(e);
})
I want to know, how much I have scroll value in pixels, because I think, scroll value depends from window size and screen resolution.
Function parameter e does not contains this information.
I can store $(window).scrollTop() after every scroll and calculate difference, but can I do it differently?
The "scroll value" does not depend on the window size or screen resolution. The "scroll value" is simply the number of pixels scrolled.
However, whether you are able to scroll at all, and the amount you can scroll is based on available real estate for the container and the dimensions of the content within the container (in this case the container is document.documentElement, or document.body for older browsers).
You are correct that the scroll event does not contain this information. It does not provide a delta property to indicate the number of pixels scrolled. This is true for the native scroll event and the jQuery scroll event. This seems like it would be a useful feature to have, similar to how mousewheel events provide properties for X and Y delta.
I do not know, and will not speculate upon, why the powers-that-be did not provide a delta property for scroll, but that is out of scope for this question (feel free to post a separate question about this).
The method you are using of storing scrollTop in a variable and comparing it to the current scrollTop is the best (and only) method I have found. However, you can simplify this a bit by extending jQuery to provide a new custom event, per this article: http://learn.jquery.com/events/event-extensions/
Here is an example extension I created that works with window / document scrolling. It is a custom event called scrolldelta that automatically tracks the X and Y delta (as scrollLeftDelta and scrollTopDelta, respectively). I have not tried it with other elements; leaving this as exercise for the reader. This works in currrent versions of Chrome and Firefox. It uses the trick for getting the sum of document.documentElement.scrollTop and document.body.scrollTop to handle the bug where Chrome updates body.scrollTop instead of documentElement.scrollTop (IE and FF update documentElement.scrollTop; see https://code.google.com/p/chromium/issues/detail?id=2891).
JSFiddle demo: http://jsfiddle.net/tew9zxc1/
Runnable Snippet (scroll down and click Run code snippet):
// custom 'scrolldelta' event extends 'scroll' event
jQuery.event.special.scrolldelta = {
delegateType: "scroll",
bindType: "scroll",
handle: function (event) {
var handleObj = event.handleObj;
var targetData = jQuery.data(event.target);
var ret = null;
var elem = event.target;
var isDoc = elem === document;
var oldTop = targetData.top || 0;
var oldLeft = targetData.left || 0;
targetData.top = isDoc ? elem.documentElement.scrollTop + elem.body.scrollTop : elem.scrollTop;
targetData.left = isDoc ? elem.documentElement.scrollLeft + elem.body.scrollLeft : elem.scrollLeft;
event.scrollTopDelta = targetData.top - oldTop;
event.scrollTop = targetData.top;
event.scrollLeftDelta = targetData.left - oldLeft;
event.scrollLeft = targetData.left;
event.type = handleObj.origType;
ret = handleObj.handler.apply(this, arguments);
event.type = handleObj.type;
return ret;
}
};
// bind to custom 'scrolldelta' event
$(window).on('scrolldelta', function (e) {
var top = e.scrollTop;
var topDelta = e.scrollTopDelta;
var left = e.scrollLeft;
var leftDelta = e.scrollLeftDelta;
// do stuff with the above info; for now just display it to user
var feedbackText = 'scrollTop: ' + top.toString() + 'px (' + (topDelta >= 0 ? '+' : '') + topDelta.toString() + 'px), scrollLeft: ' + left.toString() + 'px (' + (leftDelta >= 0 ? '+' : '') + leftDelta.toString() + 'px)';
document.getElementById('feedback').innerHTML = feedbackText;
});
#content {
/* make window tall enough for vertical scroll */
height: 2000px;
/* make window wide enough for horizontal scroll */
width: 2000px;
/* visualization of scrollable content */
background-color: blue;
}
#feedback {
border:2px solid red;
padding: 4px;
color: black;
position: fixed;
top: 0;
height: 20px;
background-color: #fff;
font-family:'Segoe UI', 'Arial';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='feedback'>scrollTop: 0px, scrollLeft: 0px</div>
<div id='content'></div>
Note that you may want debounce the event depending on what you are doing. You didn't provide very much context in your question, but if you give a better example of what you are actually using this info for we can provide a better answer. (Please show more of your code, and how you are using the "scroll value").
To detemine how many pixels were scrolled you have to keep in mind that the scroll event gets fired almost every pixel that you move. The way to accomplish it is to save the previous scrolled value and compare that in a timeout. Like this:
var scrollValue = 0;
var scrollTimeout = false
$(window).scroll(function(event){
/* Clear it so the function only triggers when scroll events have stopped firing*/
clearTimeout(scrollTimeout);
/* Set it so it fires after a second, but gets cleared after a new triggered event*/
scrollTimeout = setTimeout(function(){
var scrolled = $(document).scrollTop() - scrollValue;
scrollValue = $(document).scrollTop();
alert("The value scrolled was " + scrolled);
}, 1000);
});
This way you will get the amount of scrolled a second after scrolling (this is adjustable but you have to keep in mind that the smooth scrolling that is so prevalent today has some run-out time and you dont want to trigger before a full stop).
The other way to do this? Yes, possible, with jQuery Mobile
I do not appreciate this solution, because it is necessary to include heavy jQuery mobile. Solution:
var diff, top = 0;
$(document).on("scrollstart",function () {
// event fired when scrolling is started
top = $(window).scrollTop();
});
$(document).on("scrollstop",function () {
// event fired when scrolling is stopped
diff = Math.abs($(window).scrollTop() - top);
});
To reduce the used processing power by adding a timer to a Jquery scroll method is probably not a great idea. The visual effect is indeed quite bad.
The whole web browsing experience could be made much better by hiding the scrolling element just when the scroll begins and making it slide in (at the right position) some time after. The scrolling even can be checked with a delay too.
This solution works great.
$(document).ready(function() {
var element = $('.movable_div'),
originalY = element.offset().top;
element.css('position', 'relative');
$(window).on('scroll', function(event) {
var scrollTop = $(window).scrollTop();
element.hide();
element.stop(false, false).animate({
top: scrollTop < originalY
? 0
: scrollTop - originalY + 35
}, 2000,function(){element.slideDown(500,"swing");});
});
});
Live demo here
It's there a way to get the fill color of RaphaelJS element at a screen point(x,y)?
Thanks in advance!
Rather than attaching a click handler to every single element, you can just attach a click handler to the SVG element itself and then use RaphaelJS's getElementByPoint helper function to find the first element under the cursor. You'd do something like this:
jQuery( paper.canvas ).click( function( e )
{
var parentOffset = jQuery(this).offset();
var relX = e.pageX - parentOffset.left;
var relY = e.pageY - parentOffset.top;
console.log("Relative position: %s, %s", relX, relY);
var el = paper.getElementByPoint( e.pageX, e.pageY );
if ( el )
{
console.log( el );
}
else
{
console.log( "No hit." );
}
} );
To see an example of this in action, visit this page on my dev server and keep an eye on the console output.
One caveat: I seem to recall that you need to translate the screen coordinates into Raphael coordinates using a particular technique, depending on scroll offset -- I don't think my code handles the Y coordinate properly if the window is scrolled vertically.
Happy coding.