I am trying to achieve a simple draggable DIV with RactiveJS utilizing proxy events (on-mousedown,up,move,out)
Following JSFiddle works just fine, however when the user moves the mouse too fast the dragging stops. This is simply because the mouseevent handlers in my case are on the DIV tag instead of the body or document elements. The final idea I have is to create a slider component, however I am seeking to provide the best user experience and make this work more like JQuery's draggable.
Template:
<div class="rect {{drag ? 'dragging' : ''}}"
on-mousedown="startDrag"
on-mouseup="stopDrag"
on-mouseout="stopDrag"
on-mousemove="drag"
style="top:{{top}}px; left:{{left}}px;">
</div>
Ractive instance:
var ractive = new Ractive({
el: "#content",
template: "#template",
data: {left:20,top:80}
});
ractive.on({
startDrag : function (event) {
this.set({
'drag': true,
'mouseX': event.original.clientX - this.get('left'),
'mouseY': event.original.clientY - this.get('top')
});
},
stopDrag : function (event) {
this.set('drag', false);
},
drag : function (event) {
if(this.get('drag')) {
var x = event.original.clientX,
y = event.original.clientY;
this.set({
top: y - this.get('mouseY') ,
left: x - this.get('mouseX')
});
event.original.stopPropagation();
}
}
})
How can I improve the above code?
Usually mousemove and mouseend need to be on the document. I find that with Ractive a decorator usually works better for dragging. Here's one example to get you going (http://jsfiddle.net/h9j2hdyj/1/):
<div class="rect noSelect {{ dragging ? 'dragging' : ''}}"
decorator='drag:"position"'
style="{{#position}}top:{{top}}px;left:{{left}}px{{/}}">
</div>
decorator:
Ractive.decorators.drag = function(node, keypath){
var ractive = this, initial, begin, startPos;
node.addEventListener('mousedown', start, false)
function listenOnDocument(){
document.addEventListener('mousemove', move, false)
document.addEventListener('mouseup', unlistenOnDocument, false)
}
function unlistenOnDocument(){
document.removeEventListener('mousemove', move, false)
document.removeEventListener('mouseup', end, false)
}
function start(e){
begin = { x: e.x, y: e.y }
startPos = ractive.get(keypath)
startPos = { x: startPos.left, y: startPos.top }
ractive.set('dragging', true)
listenOnDocument()
e.preventDefault()
e.stopPropagation()
}
function move(e){
ractive.set(keypath, {
left: startPos.x + (e.x - begin.x),
top: startPos.y + (e.y - begin.y)
})
e.preventDefault()
e.stopPropagation()
}
function end(){
unlistenOnDocument()
ractive.set('dragging', false)
}
return {
update: function(pos){
console.log(pos)
position = pos
},
teardown: function(){
node.removeEventListener('mousedown', start, false)
end()
}
}
}
Another option is to use the ractive-touch plugin's pan events - they use Hammer.js under the hood so they're mouse- and touch-friendly.
Related
Question:
i would like to detect the mousemove inside the browser. When the mouse stop for 60seconds, the user will log out.
However, i would like to have a iframe (inside the login system) , but it cannot click or mousemove inside the iframe. I don't know what is the problems of iframe. Can any tell me any way to find out the mousemove action? Thank you very much.
<iframe id=iframe src=""></iframe>
http://jsfiddle.net/keshann/oqjgzsm0/518/
Check this fiddle
You can have mouse stop delay detection function as below
(function(mouseStopDelay) {
var timeout;
document.addEventListener('mousemove', function(e) {
clearTimeout(timeout);
timeout = setTimeout(function() {
var event = new CustomEvent("mousestop", {
detail: {
clientX: e.clientX,
clientY: e.clientY
},
bubbles: true,
cancelable: true
});
e.target.dispatchEvent(event);
}, mouseStopDelay);
});
}(1000));
Iframes capture mouse events, but you can transfer the events to the parent scope if the cross-domain policy is satisfied. Here's how:
// This example assumes execution from the parent of the the iframe
function bubbleIframeMouseMove(iframe) {
// Save any previous onmousemove handler
var existingOnMouseMove = iframe.contentWindow.onmousemove;
// Attach a new onmousemove listener
iframe.contentWindow.onmousemove = function(e) {
// Fire any existing onmousemove listener
if (existingOnMouseMove) existingOnMouseMove(e);
// Create a new event for the this window
var evt = document.createEvent("MouseEvents");
// We'll need this to offset the mouse move appropriately
var boundingClientRect = iframe.getBoundingClientRect();
// Initialize the event, copying exiting event values
// for the most part
evt.initMouseEvent(
"mousemove",
true, // bubbles
false, // not cancelable
window,
e.detail,
e.screenX,
e.screenY,
e.clientX + boundingClientRect.left,
e.clientY + boundingClientRect.top,
e.ctrlKey,
e.altKey,
e.shiftKey,
e.metaKey,
e.button,
null // no related element
);
// Dispatch the mousemove event on the iframe element
iframe.dispatchEvent(evt);
};
}
// Get the iframe element we want to track mouse movements on
var myIframe = document.getElementById("iframe");
// Run it through the function to setup bubbling
bubbleIframeMouseMove(myIframe);
At last have a listener
// Example use
document.getElementById('iframe').addEventListener('mousestop', function(e) {
console.log('Mouse coordinates are: ', e.detail.clientX, e.detail.clientY);
document.getElementById("message").innerHTML = 'Mouse coordinates are: ' + e.detail.clientX + ' ' + e.detail.clientY;
// The event will bubble up to parent elements.
});
and your html will be
<iframe id="iframe"></iframe>
<div id="message"></div>
function over() {
console.log("over");
}
<iframe width="300" height="300" src="http://google.com" onMouseOver="over()" />
Well here's some piece of code that does just that,
var logOut = function() {
(timeOut !== undefined)? window.clearTimeout(timeOut): null;
isLoggedIn = false;
document.removeEventListener("mousemove", setTime);
alert("Oops Logged you out");
};
var setTime = function() {
window.clearTimeout(timeOut);
timeOut = window.setTimeout(logOut, maxOut);
};
var timeOut,
isLoggedIn = true,
maxOut = 10000;
if (isLoggedIn === true) {
setTime();
document.addEventListener("mousemove", setTime);
}
Edit:
If you add this then, even if the user moves on Iframe it doesn't matter any more.
document.getElementById("myFrame").addEventListener("mousemove", function(evt) {
evt.preventDefault();
});
and here's the codepen link http://codepen.io/ram333/pen/ygLNQG
Cheers,
Rj
I have a client who wishes to track swipe events (swipe left, swipe right) on a FlexSlider photo gallery. I am using a small script to detect swipe events, and it works quite well to send an alert() or console.log() for testing purposes. However, when I tried to instead push an event to Google Tag Manager it doesn't appear to be sent.
Here is how I am attempting to track the events:
// Previous Photo
jQuery('#photo_gallery').on('swiperight', 'img', function() {
dataLayer.push({'category': 'photo-gallery', 'action' : 'photo-gallery-previous', 'label' : 'previous'});
});
// Next Photo
jQuery('#photo_gallery').on('swipeleft', 'img', function() {
dataLayer.push({'category': 'photo-gallery', 'action' : 'photo-gallery-next', 'label' : 'next'});
});
Where #photo_gallery is the ID of the standard <div class="flexslider"> container.
Here is the script I am using to create the swipeleft swiperight events:
(function($) {
$.detectSwipe = {
enabled: 'ontouchstart' in document.documentElement,
preventDefault: true,
threshold: 20
};
var startX,
startY,
isMoving = false;
function onTouchEnd() {
this.removeEventListener('touchmove', onTouchMove);
this.removeEventListener('touchend', onTouchEnd);
isMoving = false;
}
function onTouchMove(e) {
if ($.detectSwipe.preventDefault) { e.preventDefault(); }
if(isMoving) {
var x = e.touches[0].pageX;
var y = e.touches[0].pageY;
var dx = startX - x;
var dy = startY - y;
var dir;
if(Math.abs(dx) >= $.detectSwipe.threshold) {
dir = dx > 0 ? 'left' : 'right'
} else if(Math.abs(dy) >= $.detectSwipe.threshold) {
dir = dy > 0 ? 'down' : 'up'
}
if(dir) {
onTouchEnd.call(this);
$(this).trigger('swipe', dir).trigger('swipe' + dir);
}
}
}
function onTouchStart(e) {
if (e.touches.length == 1) {
startX = e.touches[0].pageX;
startY = e.touches[0].pageY;
isMoving = true;
this.addEventListener('touchmove', onTouchMove, false);
this.addEventListener('touchend', onTouchEnd, false);
}
}
function setup() {
this.addEventListener && this.addEventListener('touchstart', onTouchStart, false);
}
function teardown() {
this.removeEventListener('touchstart', onTouchStart);
}
$.event.special.swipe = { setup: setup };
$.each(['left', 'up', 'down', 'right'], function () {
$.event.special['swipe' + this] = { setup: function(){
$(this).on('swipe', $.noop);
} };
});
})(jQuery);
Note: The above script works for console logs and alerts
Does anyone have any experience tracking swipe events in Google Analytics/Tag Manager? It would be nice to tap into the FlexSlider built in swipe functionality, but I wouldn't want to modify any of the plugin code.
You should also include an "event" parameter (of, say, "swipe") to use in your GTM trigger:
dataLayer.push({
'event': 'swipe',
// your other parameters
})
Quoting from this: https://developers.google.com/tag-manager/devguide?hl=en
Google Tag Manager provides a special data layer variable called an
event that is used within JavaScript event listeners to initiate tag
firing when a user interacts with website elements such as a button.
You could then use the 'swipe' event to fire your tags.
Not sure if it is still of interest, but as an alternative to dataLayer (best option imho) you may create a "History Change" Trigger.
"Triggers based on the History Change event will fire a tag when the URL fragment (hash) changes or when a site is using the HTML5 pushstate APIs. This trigger is useful to fire tags tracking virtual pageview in an Ajax application, for instance."
https://support.google.com/tagmanager/answer/6106961?hl=en
trigger screenshot
I'm trying to get Syn.js to do a programmatic mouse movement to use in the Specs of my app.
I need to track and test a mouseenter and a mouseleave. Made a simple Spec but could not get it to work, no event is fired.
Any pointers to what I might be missing? or how to make the events fire?
My testcase:
(jsFiddle: http://jsfiddle.net/g1wjkjw5/)
JS
document.getElementById('target').addEventListener('mouseenter', log);
document.getElementById('target').addEventListener('mouseleave', log);
var events = {};
function log(e) {
console.log(e.type); // never fires... :/
events[e.type] = true;
}
setTimeout(function () {
syn.move({
from: {clientX: 400, clientY: 400},
to: {clientX: 50, clientY: 50},
duration: 1000
}, document.body);
console.log('Syn fired');
}, 200);
setTimeout(function () {
console.log(events); // this gets empty
}, 2000);
CSS
div#target {
background: #ccf;
border: 1px solid black;
}
HTML
<script src="https://rawgit.com/bitovi/syn/master/dist/syn.js"></script>
<div id="target" style="float: left; padding: 100px;"></div>
Mozilla probably won't let you
Looking at the Syn.js source code, the events you are after aren't simulated at all.
jQuery does what the others have suggested
Keep in mind that originally mouse enter/leave was proprietary to Internet Explorer, and that jQuery emulated it. Its only quite recently become ubiquitous. 1 2
Here is a jsfiddle using a modified Syn.js to achieve what you asked for.
Simply changed the event names:
if (last !== el && el && last) {
var options = syn.helpers.extend({}, point);
options.relatedTarget = el;
syn.trigger(last, 'mouseleave', options);
options.relatedTarget = last;
syn.trigger(el, 'mouseenter', options);
}
try this
document.getElementById('target').addEventListener('mouseover', function(){
console.log('mouse inside');
});
document.getElementById('target').addEventListener('mouseout', unction(){
console.log('mouse left');
});
var events = {};
setTimeout(function () {
syn.move({
from: {clientX: 600, clientY: 300},
to: {clientX: 50, clientY: 50},
duration: 1000
}, document.body);
console.log('Syn fired');
}, 200);
setTimeout(function () {
console.log(events);
}, 2000);
According to their docs the syn library fires the events mouseover and mouseout for hover events:
document.getElementById('target').addEventListener('mouseover', log);
document.getElementById('target').addEventListener('mouseout', log);
Fiddle:
http://jsfiddle.net/g1wjkjw5/2/
You can transform these events in your log function if you really want to log them as mouseenter/mouseleave
function log(e) {
if (e.type == "mouseover") {
events["mouseenter"] = true
} else if (e.type == "mouseout") {
events["mouseleave"] = true
} else {
events[e.type] = true;
}
}
http://jsfiddle.net/g1wjkjw5/4/
EDIT:
In order to capture actual mouse[enter|leave] events you could capture the mouseover/mouseout events and transform them into the proper event object:
document.getElementById('target').addEventListener('mouseover', transformEvent);
document.getElementById('target').addEventListener('mouseout', transformEvent);
function transformEvent(e) {
var newEvent = false;
if (e.type == "mouseover") {
newEvent = "mouseenter";
} else if (e.type == "mouseout") {
newEvent = "mouseleave";
}
if (newEvent) {
e.stopImmediatePropagation();
e.preventDefault();
var event = new MouseEvent(newEvent, {
'view': window,
'bubbles': true,
'cancelable': true
});
e.toElement.dispatchEvent(event);
}
}
http://jsfiddle.net/g1wjkjw5/8/
Here I have a table with two column. In 1st is draggable div object so:
$(function() {
$( "div.available" ).draggable({
snap: "div.timeline-axis-grid"
});
});
and in 2nd column is timeline table based on google visualisation API.
http://jsbin.com/oLaqoToH/5
You will see when make double click on timeline table then you create a new div object into timeline.
Now I want to simulate double click when I end dragging object from 1st column to timeline.
So simple I want to add a div object from 1st column to timeline with this little hack.
Is there any way to do this?
Is this possbile to use with jquery?
How I can simulate dobuleclick on draggable ending?
UPDATE: I dont need to simulate double clikc becouse this action has an function:
http://almende.github.io/chap-links-library/js/timeline/timeline.js
/**
* Double click event occurred for an item
* #param {Event} event
*/
links.Timeline.prototype.onDblClick = function (event) {
var params = this.eventParams,
options = this.options,
dom = this.dom,
size = this.size;
event = event || window.event;
if (params.itemIndex != undefined) {
var item = this.items[params.itemIndex];
if (item && this.isEditable(item)) {
// fire the edit event
this.trigger('edit');
}
}
else {
if (options.editable) {
// create a new item
// get mouse position
params.mouseX = links.Timeline.getPageX(event);
params.mouseY = links.Timeline.getPageY(event);
var x = params.mouseX - links.Timeline.getAbsoluteLeft(dom.content);
var y = params.mouseY - links.Timeline.getAbsoluteTop(dom.content);
// create a new event at the current mouse position
var xstart = this.screenToTime(x);
var xend = this.screenToTime(x + size.frameWidth / 10); // add 10% of timeline width
if (options.snapEvents) {
this.step.snap(xstart);
this.step.snap(xend);
}
var content = options.NEW;
var group = this.getGroupFromHeight(y); // (group may be undefined)
var preventRender = true;
this.addItem({
'start': xstart,
'end': xend,
'content': content,
'group': this.getGroupName(group)
}, preventRender);
params.itemIndex = (this.items.length - 1);
this.selectItem(params.itemIndex);
this.applyAdd = true;
// fire an add event.
// Note that the change can be canceled from within an event listener if
// this listener calls the method cancelAdd().
this.trigger('add');
if (this.applyAdd) {
// render and select the item
this.render({animate: false});
this.selectItem(params.itemIndex);
}
else {
// undo an add
this.deleteItem(params.itemIndex);
}
}
}
links.Timeline.preventDefault(event);
};
How I can use this function to drag object to timeline instead to use doubleclick simulation??? Thanks!
$( "div.available" ).draggable({
snap: "div.timeline-axis-grid",
stop: function(e, ui) {
$(this).dblclick();
}
});
This worked for me:
$(function() {
$( "div.timeline-event" ).draggable({
snap: "div.timeline-axis-grid",
stop: function(event){ timeline.onDblClick(event); }
});
});
I am trying to Simulate a scroll event using Javascript for Mobile Safari.
I am using the following code
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("scroll", true, true, window,0, 0, 0, 0, 0, false, false, false, false, 0, null);
The above code is part of a jQuery plugin jQuery.UI.Ipad which basically maps touch events like touchstart, touchmove, touchend to mouse events like mouseover, mousedown, etc
However for some reasons the code for simulating a scroll event is not working... Please help me. So essentially my question is how do I simulate the scroll event.
I think people are confused as to why you would overide the scroll control. I can see why you want to imitate mouse events, but scroll maybe should not be one of them.
Usually for scroll changes you can just get the scroll with:
var top = document.body.scrollTop;
And set with:
document.body.scrollLeft = sX;
document.body.scrollTop = sY;
So, I know I' need to simulate it too. for me it's when you have a lightbox with a overflow box that you would need it. Just one case of many I can think of. looking to for an answer. Just thought I'd share where I'm at, thou not with the jQuery.Ui.Ipad I will Google that.. but here is what I got so far and does work but not perfectly.
var inTouch=false;
var timers_arr = new Array();
var c=0;
var t;
var timer_is_on=0;
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
// jeremy's timer functions
//-------------------------------------------------------------------------------------------------------------------------------------------------------------
function clearCount(timer){
/// clear the time from timer
clearTimeout(timers_arr[timer]);
/// Make sure it's clear
timers_arr[''+timer+'']=0;
delete timers_arr[''+timer+''];
}
function setCount(timer,time,func){
clearCount(timer);
if(timers_arr[timer]==0||typeof(timers_arr[timer]) === 'undefined'){
timers_arr[timer]=setTimeout(function(){
func();
},time);
}
}
function updatePos(evt,startY){
setCount('touchmove',1,function(){
var orig = evt.originalEvent;
var curY = orig.changedTouches[0].pageY;
var y = curY - startY;
var sliderVal = $("#slider-vertical").slider("value");
sliderVal += (y*.008);
$("#slider-vertical").slider("value", sliderVal);
updatePos(evt,startY);
});
setCount('touchmove_anitMotion',200,function(){
clearCount('touchmove');
clearCount('touchmove_anitMotion');
});
}
var startX=0;
var startY=0;
var x=0;
var y=0;
var direction='';
$('body').bind("onorientationchange", updateOrientation, false);
$('#scroll-pane').live("touchstart", function(evt){
inTouch=true;
startX = event.targetTouches[0].pageX;
startY = event.targetTouches[0].pageY;
});
$('#scroll-pane').live("touchmove", function(evt){
evt.stopPropagation();
evt.preventDefault();
updatePos(evt,startY);
});
$('#scroll-pane').live("touchend", function(evt){
startX=0;
startY=0;
clearCount('touchmove');
inTouch=false;
});
$('#scroll-pane').live("touchcancel", function(evt){
startX=0;
startY=0;
clearCount('touchmove');
inTouch=false;
});
Again not perfect and looking to fix it.. but it's at the least working. Now note, this is a div that is using the jQuery UI slider for a scroll bar as (thou in mine it's vertical) shown in
http://jqueryui.com/demos/slider/#side-scroll
Hope that spurs some ideas. If I get a super stable answer I'll get back.
Cheers -Jeremy
I needed this to write a unit test , for which i need to simulate a scroll event
function dispatchScroll(target,newScrollTop) {
target.scrollTop = newScrollTop;
var e = document.createEvent("UIEvents");
// creates a scroll event that bubbles, can be cancelled,
// and with its view and detail property initialized to window and 1,
// respectively
e.initUIEvent("scroll", true, true, window, 1);
target.dispatchEvent(e);
}
For more details : https://developer.mozilla.org/en-US/docs/Web/API/event.initUIEvent
The event that worked for me was mousewheel, and the 5th parameter needs to be positive for scrolldown and negative for scrollup.
var evt = document.createEvent("MouseEvents");
evt.initMouseEvent("mousewheel", true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
Note that the event type is DOMMouseScroll for FireFox.
Events should now be created with CustomEvent as such new CustomEvent("wheel", { detail: { deltaY: 1 } }). document.createEvent is deprecated
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent
I built this utility function that scrolls by 1 pixel every frame
const startFakeScrolling = (container: HTMLDivElement) => {
const cb = () => {
container.dispatchEvent(
new CustomEvent("wheel", { detail: { deltaY: 1 } })
);
window.requestAnimationFrame(cb);
};
window.requestAnimationFrame(cb);
};
You will also need to modify the event listener to use detail, as deltaY will be undefined
const delta = e.deltaY != null ? e.deltaY : (e.detail as any).deltaY;