I have this function, on gogole maps circles, which was working perfect. But now it is no longer detecting the shiftKey, always returning false and I have no idea why.
Also tried v=3, v=3.25, v=3.26, v=3.30 and v=3.31.
<script src="//maps.googleapis.com/maps/api/js?
v=3.30&key=XXX&libraries=drawing&callback=initMap"></script>
if (myCondittion) {
myObj.addListener('click', function (event) {
if (!shiftKeyPressed(event)) {
//DoSOmething
}
} else {
//DoSomethingElse
}
});
}
function shiftKeyPressed(event) {
for (var key in event) {
if (event[key] instanceof MouseEvent) {
event["mouseEvent"] = event[key];
}
}
var isPressed = event["mouseEvent"].shiftKey;
console.log( event["mouseEvent"] );
return isPressed;
}
I wanted to add that I am also having this issue with shiftkey, altkey, and ctrlkey. I am on the experimental version and it was working just fine until about a week ago, when this was posted.
As a work around I created global variables:
var _keydownEvent;
var setKeydownEvent = function(e){
_keydownEvent = e;
}
var getKeydownEvent = function(){
return _keydownEvent;
}
app.setKeydownEvent = setKeydownEvent;
app.getKeydownEvent = getKeydownEvent;
Global listeners:
$(window).bind('keydown', function(e){
app.setKeydownEvent(e);
});
$(window).bind('keyup', function(e){
app.setKeydownEvent(e);
});
In my map:
getCtrlKeyStatus: function(e) {
var ret = false;
var global_event = app.getKeydownEvent();
if (typeof(global_event) === 'object' && global_event.ctrlKey) {
ret = true;
}
return ret;
},
Related
I've been trying to get mithril touch to work using a good code on github:
https://gist.github.com/webcss/debc7b60451f2ad2af41
import m from 'mithril'
/*****************************************
/* DOM touch support module
/*****************************************/
if (!window.CustomEvent) {
window.CustomEvent = function (event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
return evt;
};
window.CustomEvent.prototype = window.Event.prototype;
}
(function(document) {
var TAPTRESHOLD = 200, // time within a double tap should have happend
TAPPRECISION = 60 / 2, // distance to identify a swipe gesture
touch = { },
tapCount = 0, // counts the number of touchstart events
tapTimer = 0, // timer to detect double tap
isTouchSwipe = false, // set to true whenever
absolute = Math.abs,
touchSupported = 'ontouchstart' in window;
function parentIfText (node) {
return 'tagName' in node ? node : node.parentNode;
}
function dispatchEvent(type, touch) {
if(touchSupported) {
touch.originalEvent.preventDefault();
touch.originalEvent.stopImmediatePropagation();
}
var event = new CustomEvent(type, {
detail: touch,
bubbles: true,
cancelable: true
});
touch.target.dispatchEvent(event);
console.log(type);
touch = { };
tapCount = 0;
return event;
}
function touchStart(e) {
if( !touchSupported || e.touches.length === 1) {
var coords = e.targetTouches ? e.targetTouches[0] : e;
touch = {
originalEvent: e,
target: parentIfText(e.target),
x1: coords.pageX,
y1: coords.pageY,
x2: coords.pageX,
y2: coords.pageY
};
isTouchSwipe = false;
tapCount++;
if (!e.button || e.button === 1) {
clearTimeout(tapTimer);
tapTimer = setTimeout(function() {
if(absolute(touch.x2 - touch.x1) < TAPPRECISION &&
absolute(touch.y2 - touch.y2) < TAPPRECISION &&
!isTouchSwipe) {
dispatchEvent((tapCount===2)? 'dbltap' : 'tap', touch);
clearTimeout(tapTimer);
}
tapCount = 0;
}, TAPTRESHOLD);
}
}
}
function touchMove(e) {
var coords = e.changedTouches ? e.changedTouches[0] : e;
isTouchSwipe = true;
touch.x2 = coords.pageX;
touch.y2 = coords.pageY;
/* the following is obsolete since at least chrome handles this
// if movement is detected within 200ms from start, preventDefault to preserve browser scroll etc.
// if (touch.target &&
// (absolute(touch.y2 - touch.y1) <= TAPPRECISION ||
// absolute(touch.x2 - touch.x1) <= TAPPRECISION)
// ) {
// e.preventDefault();
// touchCancel(e);
// }
*/
}
function touchCancel(e) {
touch = {};
tapCount = 0;
isTouchSwipe = false;
}
function touchEnd(e) {
var distX = touch.x2 - touch.x1,
distY = touch.y2 - touch.y1,
absX = absolute(distX),
absY = absolute(distY);
// use setTimeout here to register swipe over tap correctly,
// otherwise a tap would be fired immediatly after a swipe
setTimeout(function() {
isTouchSwipe = false;
},0);
// if there was swipe movement, resolve the direction of swipe
if(absX || absY) {
if(absX > absY) {
dispatchEvent((distX<0)? 'swipeleft': 'swiperight', touch);
} else {
dispatchEvent((distY<0)? 'swipeup': 'swipedown', touch);
}
}
}
document.addEventListener(touchSupported ? 'touchstart' : 'mousedown', touchStart, false);
document.addEventListener(touchSupported ? 'touchmove' : 'mousemove', touchMove, false);
document.addEventListener(touchSupported ? 'touchend' : 'mouseup', touchEnd, false);
// on touch devices, the taphold complies with contextmenu
document.addEventListener('contextmenu', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
dispatchEvent('taphold', {
originalEvent: e,
target: parentIfText(e.target)
});
}, false);
if (touchSupported) {
document.addEventListener('touchcancel', touchCancel, false);
}
}(window.document));
m.touchHelper = function(options) {
return function(element, initialized, context) {
if (!initialized) {
Object.keys(options).forEach(function(touchType) {
element.addEventListener(touchType, options[touchType], false);
});
context.onunload = function() {
Object.keys(options).forEach(function(touchType) {
element.removeEventListener(touchType, options[touchType], false);
});
};
}
};
};
The only thing I've added is import m from 'mithril'.
Launching the app I can see that everything is registering as should through the console, however, in my main code I want to use that touch data:
const app = {
view: vnode => {
return m('.main', {config: m.touchHelper({ 'tap': consoleLog})
}
}
function consoleLog() {
console.log('Triggered')
}
However, this function is not triggered.
I don't know how much Mithril has changed since February 2017 (I have only recently picked up Mithril), but that m.touchHelper seems weird. What are element, initialized and context? Why is a function returned; is it ever called?
Using m.touchHelper with the config attribute seems also weird. When you do this:
return m('.main', {config: m.touchHelper({'tap': consoleLog})})
the resulting DOM element looks like this:
<div config="function(element, initialized, context) {
if (!initialized) {
Object.keys(options).forEach(function(touchType) {
element.addEventListener(touchType, options[touchType], false);
});
context.onunload = function() {
Object.keys(options).forEach(function(touchType) {
element.removeEventListener(touchType, options[touchType], false);
});
};
}
}" class="main"></div>
See JSFiddle with your code. (Note that I added missing }) to the end of app's view method.)
I would change m.touchHelper to something like this:
m.touchHelper = function(vnode, options) {
if (vnode.state.initialized) return;
vnode.state.initialized = true;
Object.keys(options).forEach(function(touchType) {
vnode.dom.addEventListener(touchType, options[touchType], false);
});
// Note that I removed the `context.unload` part as I'm unsure what its purpose was
};
And call it in a component's oncreate lifecycle method:
const app = {
oncreate(vnode) {
m.touchHelper(vnode, {'tap': consoleLog});
},
view: vnode => {
return m('.main');
}
};
Then it seems to work. See JSFiddle with updated code.
I have been trying to trigger right click even when the users left click.
I have tried trigger, triggerHandler, mousedown but I wasn't able to get it to work.
I'm able to catch the click events themselves but not able to trigger the context menu.
Any ideas?
To trigger the mouse right click
function triggerRightClick(){
var evt = new MouseEvent("mousedown", {
view: window,
bubbles: true,
cancelable: true,
clientX: 20,
button: 2
});
some_div.dispatchEvent(evt);
}
To trigger the context menu
function triggerContextMenu(){
var evt = new MouseEvent("contextmenu", {
view: window
});
some_div.dispatchEvent(evt);
}
Here is the bin: http://jsbin.com/rimejisaxi
For better reference/explanation: https://stackoverflow.com/a/7914742/1957036
Use the following code for reversing mouse clicks.
$.extend($.ui.draggable.prototype, {
_mouseInit: function () {
var that = this;
if (!this.options.mouseButton) {
this.options.mouseButton = 1;
}
$.ui.mouse.prototype._mouseInit.apply(this, arguments);
if (this.options.mouseButton === 3) {
this.element.bind("contextmenu." + this.widgetName, function (event) {
if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
$.removeData(event.target, that.widgetName + ".preventClickEvent");
event.stopImmediatePropagation();
return false;
}
event.preventDefault();
return false;
});
}
this.started = false;
},
_mouseDown: function (event) {
// we may have missed mouseup (out of window)
(this._mouseStarted && this._mouseUp(event));
this._mouseDownEvent = event;
var that = this,
btnIsLeft = (event.which === this.options.mouseButton),
// event.target.nodeName works around a bug in IE 8 with
// disabled inputs (#7620)
elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
return true;
}
this.mouseDelayMet = !this.options.delay;
if (!this.mouseDelayMet) {
this._mouseDelayTimer = setTimeout(function () {
that.mouseDelayMet = true;
}, this.options.delay);
}
if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
this._mouseStarted = (this._mouseStart(event) !== false);
if (!this._mouseStarted) {
event.preventDefault();
return true;
}
}
// Click event may never have fired (Gecko & Opera)
if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
$.removeData(event.target, this.widgetName + ".preventClickEvent");
}
// these delegates are required to keep context
this._mouseMoveDelegate = function (event) {
return that._mouseMove(event);
};
this._mouseUpDelegate = function (event) {
return that._mouseUp(event);
};
$(document)
.bind("mousemove." + this.widgetName, this._mouseMoveDelegate)
.bind("mouseup." + this.widgetName, this._mouseUpDelegate);
event.preventDefault();
mouseHandled = true;
return true;
}
});
Now at the function calling event use mouseButton : 3 for right click and 1 for left click
on my website I have a div .toggle-search that if you click on it it expands to .search-expand where a search form is. This is the code in jQuery
/* Toggle header search
/* ------------------------------------ */
$('.toggle-search').click(function(){
$('.toggle-search').toggleClass('active');
$('.search-expand').fadeToggle(250);
setTimeout(function(){
$('.search-expand input').focus();
}, 300);
});
Now the only way to close the .search-expand is to click once again on the .toggle-search. But I want to change that it closes if you click anywhere else on the site. For an easier example I have the Hueman theme, and I'm talking about the top right corner search option. http://demo.alxmedia.se/hueman/
Thanks!
Add the event on all elements except the search area.
$('body *:not(".search-expand")').click(function(){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});
or another way,
$('body').click(function(e){
if(e.target.className.indexOf('search-expand') < 0){
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
}
});
var isSearchFieldOpen = false;
var $toggleSearch = $('.toggle-search');
var $searchExpand = $('.search-expand');
function toggleSearch() {
// Reverse state
isSearchFieldOpen = !isSearchFieldOpen;
$toggleSearch.toggleClass('active');
// You can use callback function instead of using setTimeout
$searchExpand.fadeToggle(250, function() {
if (isSearchFieldOpen) {
$searchExpand.find('input').focus();
}
});
}
$toggleSearch.on('click', function(e) {
e.stopPropagation();
toggleSearch();
});
$(document.body).on('click', function(e) {
if (isSearchFieldOpen) {
var target = e.target;
// Checking if user clicks outside .search-expand
if (!$searchExpand.is(target) && !$searchExpand.has(target).length) {
toggleSearch();
}
}
});
I have a second search on the site with the same code as before only
with div .toggle-serach2 and .expand-search2, how can i make your code
so it wont overlap. just changing the name to $('toggle-search2')
doesn't cut it
in that case, I would suggest you convert your code into a plugin:
(function($, document) {
var bodyHandlerAttached = false;
var openedForms = [];
var instances = {};
var defaults = {
activeClass: 'active'
};
function ToggleSearch(elem, options) {
this.options = $.extend({}, defaults, options);
this.$elem = $(elem);
this.$btn = $(options.toggleBtn);
this.isOpen = false;
this.id = generateId();
this.bindEvents();
instances[this.id] = this;
if (!bodyHandlerAttached) {
handleOutsideClick();
bodyHandlerAttached = true;
}
}
ToggleSearch.prototype = {
bindEvents: function() {
this.$btn.on('click', $.proxy(toggleHandler, this));
},
open: function() {
if (this.isOpen) { return; }
var _this = this;
this.$btn.addClass(this.options.activeClass);
this.$elem.fadeIn(250, function() {
_this.$elem.find('input').focus();
});
openedForms.push(this.id);
this.isOpen = true;
},
close: function(instantly) {
if (!this.isOpen) { return; }
this.$btn.removeClass(this.options.activeClass);
if (instantly) {
this.$elem.hide();
} else {
this.$elem.fadeOut(250);
}
openedForms.splice(openedForms.indexOf(this.id), 1);
this.isOpen = false;
},
toggle: function() {
if (this.isOpen) {
this.close();
} else {
this.open();
}
}
};
var toggleHandler = function(ev) {
ev.stopPropagation();
this.toggle();
};
var handleOutsideClick = function(e) {
$(document.body).on('click', function(e) {
if (openedForms.length) {
var target = e.target;
var instance;
for (var id in instances) {
instance = instances[id];
if (!instance.$elem.is(target) && !instance.$elem.has(target).length) {
instance.close(true);
}
}
}
});
};
function generateId() {
return Math.random().toString(36).substr(2, 8);
}
$.fn.toggleSearch = function(options) {
return this.each(function() {
if (!$.data(this, 'toggleSearch')) {
$.data(this, 'toggleSearch', new ToggleSearch(this, options));
}
});
};
})(window.jQuery, document);
And then use it like this:
$('.search-expand').toggleSearch({
toggleBtn: '.toggle-search'
});
$('.search-expand2').toggleSearch({
toggleBtn: '.toggle-search2'
});
JSFiddle example
You could add a click handler to the main window that removes the active class:
$(window).click(function(){
$('.toggle-search').removeClass('active');
}
and then prevent the class removal when you click inside of your toggle-search elem
$('.toggle-search').click(function(e){
e.stopPropagation();
// remainder of click code here
)};
Try to add body click listener
$('body').click(function(e){
if ($(e.target).is('.toggle-search')) return;
$('.toggle-search').removeClass('active');
$('.search-expand').fadeOut(250);
});
This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
What is the difference of using addEventListener?
I noticed that events can be assigned directly on objects, without having to use addEventListener:
document.onload = function(e){
// do stuff..
};
instead of:
document.addEventListener('load', function(e){
// do stuff..
});
So is there any reason I shouldn't use the first method? Why don't other people use it?
Also this appear to work in old IE too (in which you needed attachEvent).
Consider what happens if you try the following (I'm attaching the events to window because that is where you should listen for this event)
window.onload = function (e) {console.log('A');};
window.onload = function (e) {console.log('B');};
vs
window.addEventListener('load', function (e) {console.log('C');}, false);
window.addEventListener('load', function (e) {console.log('D');}, false);
From the first code block you will only see "B", but from the second you'll see both "C" and "D". Fiddle (please open console to see).
Apart from the fact that, as the accepted answer shows, directly binding handlers to the DOM limits the amout of handlers, addEventListener has a lot more to offer:
The event listener needn't be bound to the element directly (it doesn't have to exist in the DOM). This is useful when using ajax (think of it as jQuery's .on method).
A single listener can deal with all events of a particular type, so using event listeners requires less resources (which can speed up the overall performance)
For X-browser compatibility (like IE8), it's a lot easier to avoid memory-leaks:
window.onload = function(e)
{
alert('In IE8, this causes mem-leaks!');
};
var load = function(e)
{//cf #PaulS.'s comment & link on IE8 and symbol bleeding
e = e || window.event;//X-browser stuff
var target = e.target || e.srcElement;//so you can use this callback for all browsers
if (window.removeEventListener)
{//more X-browser stuff
return window.removeEventListener('load',load,false);
}
window.detachEvent('onload',load);//reference callback by variable name
};
if (window.addEventListener)
{
window.addEventListener('load',load,false);
}
else
{//in IE8, addEventListener doesn't exist, but it has a jScript counterpart:
//no mem-leaks in IE AFAIK
window.attachEvent('onload', load);
}
Here's a couple of links which might interest you (Yes, I know, shameless self-promotion - sorry):
Why do we need event listeners?
mem-leaks & event delegation & closures in IE8
And just for fun: a script I wrote a while back that uses event delegation and works on IE, FF, chrome, ... and touch devices. Which was a bit more tricky than I expected.
/**
* Copyright 2012, Elias Van Ootegem
* Date: Tue Jul 03 2012 +0100
*/
(function(G,undef)
{
'use strict';
var load,clickHandler,touchHandler,hide,reveal;
hide = function(elem)
{
elem.setAttribute('style','display:none;');
};
reveal = function(show,nextTo)
{
var str = 'display: block; position:relative; left:220px; top: ' + (nextTo.offsetTop - show.parentNode.offsetTop) + 'px;';
show.setAttribute('style',str);
}
load = function()
{
var doc = G.document;
if (G.removeEventListener)
{
G.removeEventListener('load',load,false);
}
else
{
G.detachEvent('onload',load);
}
if (doc.hasOwnProperty('ontouchstart'))
{//We have a touch device
touchHandler = (function(subNavs)
{
var current,divs = (function()
{
var i,r = {};
for (i=0;i<subNavs.length;i++)
{
r[subNavs[i].id] = doc.getElementById(subNavs[i].id + 'd');
hide(r[subNavs[i].id]);
}
return r;
}());
return function(e)
{
e = e || G.event;
if (e.changedTouches.length !== 1)
{//multi-touch
return e;
}
var timer,endListener,coords,target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'img' && target.id.match(/^close[0-9]+$/))
{
hide(current);
current = undef;
return e;
}
if (target.tagName.toLowerCase() === 'a')
{
target = target.parentNode;
}
if (target.tagName.toLowerCase() !== 'p' || !target.id || !divs[target.id])
{
if (current === undef)
{
return e;
}
while(target !== doc.body)
{
target = target.parentNode;
if (target === current)
{
return e;
}
}
timer = setTimeout(function()
{
doc.body.removeEventListener('touchend',endListener,false);
clearTimeout(timer);
timer = undef;
},300);
endListener = function(e)
{
doc.body.removeEventListener('touchend',endListener,false);
clearTimeout(timer);
timer = undef;
hide(current);
current = undef;
return e;
};
return doc.body.addEventListener('touchend',endListener,false);
}
coords = {x:e.changedTouches[0].clientX,y:e.changedTouches[0].clientY};
timer = setTimeout(function()
{
doc.body.removeEventListener('touchend',endListener,false);
clearTimeout(timer);
timer = undef;
},300);
endListener = function(e)
{
e = e || G.event;
clearTimeout(timer);
timer = undef;
doc.body.removeEventListener('touchend',endListener,false);
var endCoords,endTarget = e.target || e.srcElement;
if (endTarget !== target)
{
endCoords = {x:e.changedTouches[0].clientX,y:e.changedTouches[0].clientY};
if (Math.abs(coords.x - endCoords.x) < 26 && Math.abs(coords.y - endCoords.y) < 26)
{
endTarget = target;
}
}
if (endTarget !== target)
{
return e;
}
if (current !== undef)
{
hide(current);
current = undef;
}
current = divs[target.id];
reveal(current,target);
};
doc.body.addEventListener('touchend',endListener,false);
};
}(doc.getElementsByClassName('subnavbar')));
return doc.body.addEventListener('touchstart',touchHandler,false);
}
clickHandler = (function(subNavs)
{
var current,divs = (function()
{
var i,r = {};
for (i=0;i<subNavs.length;i++)
{
r[subNavs[i].id] = doc.getElementById(subNavs[i].id + 'd');
hide(r[subNavs[i].id]);
}
return r;
}());
return function(e)
{
e = e || G.event;
var target = e.target || e.srcElement;
if (target.tagName.toLowerCase() === 'img' && target.id.match(/^close[0-9]+$/))
{
hide(current);
current = undef;
return e;
}
if (target.tagName.toLowerCase() === 'a')
{
target = target.parentNode;
}
if (target.tagName.toLowerCase() !== 'p' || !target.className.match(/\bsubnavbar\b/))
{
if (current !== undef)
{
target = (function()
{
while (target !== doc.body)
{
target = target.parentNode;
if (target === current)
{
return current;
}
}
}());
if (target !== current)
{
hide(current);
current = undef;
}
}
return e;
}
if (e.preventDefault)
{
e.preventDefault();
e.stopPropagation();
}
else
{
e.returnValue = false;
e.cancelBubble = true;
}
if (current !== undef)
{
hide(current);
}
current = divs[target.id];
reveal(current,target);
};
}(doc.getElementsByClassName('subnavbar')));
if (doc.body.addEventListener)
{
return doc.body.addEventListener('click',clickHandler,false);
}
return doc.body.attachEvent('onclick',clickHandler);
};
if (G.addEventListener)
{
return G.addEventListener('load',load,false);
}
return G.attachEvent('onload',load);
}(this));
I am trying to be able to tab through these tooltip as a part of my tab order on my page but the tabbing stops at the first tooltip. Is the problem with the try catch statement in my Javascript code?
landingTooltip = {
locked :false,
initialize: function(){
if (dojo.byId('pickup-cycle')){
this.buttons = dojo.query('.button-link', dojo.byId('pickup- cycle'));
}
else{
return;
}
var _this = this;
dojo.forEach(
_this.buttons,
function(obj){
Hoverable.disconnect(obj)
domExtender.connect(obj,"onmouseenter",function(e){
_this.show(domExtender.closest(obj, '.step'), obj);
});
// dojo.connect(obj,"onmouseleave",function(e){
// _this.hide(domExtender.closest(obj, '.step'), obj, null);
// });
domExtender.connect(dojo.query('a', obj)[0],"onfocus",function(e){
_this.show(domExtender.closest(obj, '.step'), obj);
});
}
);
},
show: function(el, t){
if (this.locked == true){
return;
}
this.locked = true;
var t = t;
var _this = this;
dojo.addClass(dojo.query('span', t)[0], 'hover');
this.tt = dojo.query('.tool-tip', el)[0];
var placed = dojo.place(
_this.tt,
dojo.body(),
'first'
);
dojo.style(_this.tt, 'display', 'block');
_this.tt.focus();
var setFocus = dojo.query('h5', placed)[0];
setFocus.focus();
this.inst = dojo.connect(_this.tt,"onblur",function(e){
if (domExtender.closest(e.target, 'div.tool-tip') == null) {
_this.hide(domExtender.closest(t, '.step'), t, true);
}
});
this.inst1 = dojo.connect(dojo.body(), 'onclick',function(e){
//alert(domExtender.closest(e.target, 'div.tool-tip'));
if (domExtender.closest(e.target, 'div.tool-tip') == null){
_this.hide(domExtender.closest(t, '.step'), t, true);
}
});
},
hide: function(el, t, blur){
// if (this.locked == true){
// return;
// }
this.locked = true;
var _this = this;
if (this.inst){
dojo.disconnect(_this.inst);
}
if (this.inst1){
dojo.disconnect(_this.inst1);
}
dojo.removeClass(dojo.query('span', t)[0], 'hover');
var placed = dojo.place(
_this.tt,
el,
'first'
);
dojo.style(placed, 'display', 'none');
_this.locked = false;
try {
var setFocus = domExtender.next(el);
setFocus.focus();
} catch (e) {
}
}
}
Install firebug
Set a breakpoint
See what exactly is causing tab order to break.
Report a new specific question here.