disable webapp scrolling without killing click (tap) events in javascript - javascript

This is my first post so ill try to explain it clear:
Im working on a web application, but the main point is, that i want let my users feel like its a native app. In a native app you cant scroll like in iOS safari so i tried to disable scrolling with event.preventDefault. This works great except that form elements and links arent tapable anymore. My solution to that was this little script, but if you start a touch on one of the escaped elements, it scrolls anyway. Not a big deal but its driving me insane...
notes to script:
isTouch returns true/false if its a touchable device
the .contains method returns true/false if an array contains a string
if (isTouch) {
window.addEventListener("touchstart", function (evt) {
var target = evt.touches[0].target;
var tags = 'a input textarea button'.split(' ');
if ( tags.contains(target.tagName) === false ) {
evt.preventDefault();
}
}, false);
}
EDIT
My main question is, is there a solution to fire the tap event without a touchmove event to allow scrolling
EDIT 2
I solved the problem. My solution is, to emulate the events on interactive elements:
var eventFire = function (el, etype) {
if (el.fireEvent) {
(el.fireEvent('on' + etype));
}
else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
if (isTouch) {
window.addEventListener("touchstart", function (evt) {
var target = evt.touches[0].target;
var foc = 'input textarea'.split(' ');
var clck = 'a button'.split(' ');
if ( foc.contains(target.tagName) ) {
target.focus();
eventFire(target,'click');
evt.preventDefault();
}
else {
evt.preventDefault();
}
}, false);
}

Related

Keypress Gestures using Tampermonkey for Chrome

I'm working on a tampermonkey userscript to replace a feature that existed with FireGestures back in Pre-Quantum Firefox. The ability to open all hovered links into new background tabs. So having a combination keypress, which in FG was Ctrl + Right Click and drawing a gesture trail though every link you wanted open. Everything I have so far has been written for me by somebody else so I'm not trying to take credit and I in am way over my head. I don't have the know-how to edit and fix what is needed. This is what I have so far.
(function(delay, t, lnk, clicked) {
//config: delay before click. mouse movement will reset the delay timer.
delay = 1000; //in milliseconds. 1sec = 1000ms
t = 0;
function mousemove() {
clearTimeout(t);
if (lnk) t = setTimeout(clickLink, delay);
}
function clickLink() {
removeEventListener("mousemove", mousemove);
clearTimeout(t);
if (lnk) {
lnk.target = "_blank";
lnk.click();
lnk.target = "";
clicked = true;
}
}
addEventListener("mouseover", function(ev, el, el2) {
el = ev.target;
removeEventListener("mousemove", mousemove);
clearTimeout(t);
while (el) {
if (el.tagName === "A") {
el2 = el;
if (el !== lnk) {
lnk = el;
clicked = false;
addEventListener("mousemove", mousemove);
clearTimeout(t);
t = setTimeout(clickLink, delay);
}
return;
}
el = el.parentNode;
}
if (!el2) {
lnk = null;
clicked = false;
removeEventListener("mousemove", mousemove);
clearTimeout(t);
}
});
})();
There is a couple issues I face.
1. This doesn't require any sort of button combination. It is continually active and will click any link that is hovered over for the specified length of time. I would prefer it to only function when a button combination is pressed, ideally Ctrl + Rightclick. I found a thread dealing with combination keypresses but wouldn't know how to edit it and insert it into the existing script to fit my needs.
document.addEventListener ("keydown", function (zEvent) {
if (zEvent.ctrlKey && zEvent.altKey && zEvent.code === "KeyE") {
// DO YOUR STUFF HERE
}
} );
2. The pop-up blocker in chrome actually prevents these tabs from opening. I don't know if there is any way of remedying this other than turning off the pop-up blocker, but if there was I'd appreciate the help
3. This script opens up tabs in the foreground rather than the background. So opening up a bunch of links on a page wouldn't be possible because it would navigate to the new tab as soon as the first link is clicked. My original idea for fixing this was to just have the script just do a middle-click mouse event over every link it passed over, but I don't even know if that is something that is possible or practical.
I know I am asking a lot but I was just hoping that someone out there that knows what they are doing could help me out by either editing what I already have or writing something out themselves. I appreciate any help provided.
here's my spin on it. This is toggled rather than going on while you are holding onto the keys.
you could had the following to your TamperMonkey script, and when you press "Ctrl + Alt + S", the links on the page are modified and appended a onmouseover event. when you hit the key combination again, the event gets removed from the link. Short and simple.
document.addEventListener('keydown', function (zEvent) {
if (zEvent.ctrlKey && zEvent.altKey && zEvent.code === 'KeyS') {
var links = document.getElementsByTagName('a');
console.log(links.length);//how many links have been grabbed
for (var i = 0; i < links.length; i++) {
if (links[i].onmouseover !== null && links[i].onmouseover.toString().indexOf('function openit') > -1)
{
//toggling the funcitonality off
//remove it
links[i].setAttribute('target', '');
links[i].setAttribute('onmouseover', '');
}
else
{
//toggling the funcitonality on
//add it
links[i].setAttribute('target', 'blank');
links[i].setAttribute('onmouseover', 'function openit(elem){console.log(\'userScript will click on the link\');elem.click();};openit(this);');
}
}
}
}
);
As for popup blocking... I don't know.

How to disable the browser back button using javascript in a HTML page. Can we get any callback method trigerred on click of backbutton on browser

How to disable the browser back button using javascript in a HTML page. Can we get any callback method trigerred on click of backbutton on browser using Javascript and not using Jquery Mobile library.
Solution would be really appreciated. I tried with few solutions online, but nothing seemed to work.
You should never do that. https://www.irt.org/script/311.htm
By the way, you may just warn the user using window.onbeforeunload.
You can-not actually disable browser back button. And there is no event for capturing the back button click.
If it is really necessary you can do something like that:
(function (global) {
var _extra_hash = "!";
var noBack = function () {
global.location.href += "#";
global.setTimeout(function () {
global.location.href += _extra_hash;
}, 50);
};
global.onhashchange = function () {
if (global.location.hash !== _extra_hash) {
global.location.hash = _extra_hash;
}
};
global.onload = function () {
noBack();
// this is for disabling backspace on page except on input fields and textarea..
/*document.body.onkeydown = function (e) {
var elm = e.target.nodeName.toLowerCase();
if (e.which === 8 && (elm !== 'input' && elm !== 'textarea')) {
e.preventDefault();
}
// stopping event bubbling up the DOM tree..
e.stopPropagation();
};*/
}
})(window);
But the user can still kill the tab. Anyway, It is generally a bad idea overriding the default behavior of web browser.

Temporarily disable touchstart event

I have a mobile based web application. Currently I am encountering an issue when ajax calls are being made. The wait spinner which is enclosed in a div can be clicked through on the ipad device. The javascript event being triggered is touchstart. Is there anyway to prevent this event from going through normal processing?
Tried to call the following, however it did not work.
Disable
document.ontouchstart = function(e){ e.preventDefault(); }
Enable
document.ontouchstart = function(e){ return true; }
How touchstart is handled
$(document).on('touchstart', function (eventObj) {
//toggle for view-icon
if (eventObj.target.id == "view-icon") {
$("#view-dropdown").toggle();
} else if ($(eventObj.target).hasClass("view-dropdown")) {
$("#view-dropdown").show();
} else {
$("#view-dropdown").hide();
}
});
As user3032973 commented, you can use a touchLocked variable, which is working perfectly.
I have used it in combination with the Cordova Keyboard-Plugin. Scrolling will be disabled the time the keyboard is shown up and reenabled the time the keyboard is hiding:
var touchLocked = false;
Keyboard.onshowing = function () {
touchLocked = true;
};
Keyboard.onhiding = function () {
touchLocked = false;
};
document.ontouchstart = function(e){
if(touchLocked){
e.preventDefault();
}
};

Detect single tap in UIWebView, but still support text selection and links

I'm using JavaScript to detect taps in a page I'm showing in a UIWebView, like so:
<div id="wrapper">
Apple
</div>
<script>
document.getElementById("wrapper").addEventListener('click', function() {
document.location = 'internal://tap';
}, false);
</script>
I'm intercepting links with my web view delegate, and look for "internal://tap". When I get that, I prevent the web view from navigating, and respond to the tap. However doing this I lose the ability to select text. Tapping the link does still work correctly.
In fact, just adding an event listener for 'click' removes the ability to select text, even if the handler doesn't attempt to change the document location.
Any idea what I'm doing wrong?
Apparently if you put a click listener on an element, you can no longer select text within that element on iOS. My solution was to detect taps using a combination of touchstart, touchmove, and touchend events, along with a timer to ignore multi-taps, and checking the current document selection to make sure a selection event is not going on.
Here's the JS code I used:
SingleTapDetector = function(element, handler) {
this.element = element;
this.handler = handler;
element.addEventListener('touchstart', this, false);
};
SingleTapDetector.prototype.handleEvent = function(event) {
switch (event.type) {
case 'touchstart': this.onTouchStart(event); break;
case 'touchmove': this.onTouchMove(event); break;
case 'touchend': this.onTouchEnd(event); break;
}
};
SingleTapDetector.prototype.onTouchStart = function(event) {
this.element.addEventListener('touchend', this, false);
document.body.addEventListener('touchmove', this, false);
this.startX = this.currentX = event.touches[0].clientX;
this.startY = this.currentY = event.touches[0].clientY;
this.startTime = new Date().getTime();
};
SingleTapDetector.prototype.onTouchMove = function(event) {
this.currentX = event.touches[0].clientX;
this.currentY = event.touches[0].clientY;
};
SingleTapDetector.prototype.onTouchEnd = function(event) {
var that = this;
// Has there been one or more taps in this sequence already?
if (this.tapTimer) {
// Reset the timer to catch any additional taps in this sequence
clearTimeout(this.tapTimer);
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
}, 300);
} else {
// Make sure the user didn't move too much
if (Math.abs(this.currentX - this.startX) < 4 &&
Math.abs(this.currentY - this.startY) < 4) {
// Make sure this isn't a long press
if (new Date().getTime() - this.startTime <= 300) {
// Make sure this tap wasn't part of a selection event
if (window.getSelection() + '' == '') {
// Make sure this tap is in fact a single tap
this.tapTimer = setTimeout(function() {
that.tapTimer = null;
// This is a single tap
that.handler(event);
}, 300);
}
}
}
}
};
new SingleTapDetector(document.body, function(event) {
document.location = "internal://tap";
});
There is no need to use Javascript for this, it's overkill when the UIGestureRecognizerDelegate has adequate methods. All you need to do is make sure that when text selection is taking place, the tap recogniser isn't triggered.
- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
BOOL hasTap = ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]);
BOOL hasLongTouch = ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] ||
[otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]);
if (hasTap && hasLongTouch) {
// user is selecting text
return NO;
}
return YES;
}
That takes care of text selection, and links should work fine anyway (at least they do for me).

The Problem of Selecting Unread Messages in Gmail with JQuery

In Gmail, clicking on the checkbox shown below selects all messages and I'm making a userscript (for personal use and I need it to work in Chrome) that'll select the unread messages only (only the first 2 messages in the screenie below are unread) instead of the default behavior of that checkbox.
My first idea is to simulate click events and although I could access the "unread" menuitem fine using the code...
var unread_menuitem = document.getElementById('canvas_frame').contentWindow.document.getElementById(':s2');
$(unread_menuitem).css({'border':'thin red solid'});
and dispatch the click event to it using the code...
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent( 'click', true, true );
unread_menuitem.dispatchEvent(clickEvent); // Chrome's console returned 'true'
the unread messages don't get selected.
My second idea was to brute force the selection by checking the checkbox $('#canvas_frame').contents().find('tr.zE input').prop('checked', true) and apply the css styles that Gmail applies on a manual click event, but while I was able to match the manual click event both visually as well as DOM-wise (afaik)...
Gmail says "No conversations selected" while performing some action, in this case I did a "Mark as Read". I also want to note that manually clicking on the checkboxes that were put in this state using my brute force method did not "uncheck" them as you'd expect. They needed one additional manual click to get unchecked.
Both my ideas have bombed and I want to know if there are others ways to tackle this, or if there are ways to improve upon my ideas above that can solve the problem.
There's a script here that looks it does what you're trying to do. Is that the whole script you needed to create or was that just part of the functionality?
According to the discussions, they did create it for Firefox which it works in, some people have commented it doesn't work in Chrome, so you might be looking at a solution that needs to target different browsers (your question doesn't specify if it must work in Chrome, just that you were using it).
This is what they are using to select the unread messages, it looks like they are simulating the mousedown and mouseup events on each item:
var handler = function(type,e) {
e.preventDefault();
e.stopPropagation();
var e2 = document.createEvent('MouseEvents');
e2.initEvent('mousedown',true,false);
var el = anchor.wrappedJSObject;
el.dispatchEvent(e2);
setTimeout(function() {
var el = document.querySelectorAll('div[selector='+type+'] > div')[0].wrappedJSObject;
var e2 = document.createEvent('MouseEvents');
e2.initEvent('mouseup',true,false);
el.dispatchEvent(e2);
},100);
}
They are calling this by setting up a click event further down which calls handler('unread',e);
Solved it, easy as pie and now that I think about it, it's the solution listed in user PirateKitten's answer - instead of "checking off" the checkboxes besides the unread messages like my second idea, simulate clicks on those checkboxes instead. Works like a charm and here's the code, which you can run in Chrome's console while using Gmail (doesn't need jQuery btw):
var unreadMessages = document.getElementById('canvas_frame').contentWindow.document.querySelectorAll('tr.zE input');
var numMessages = unreadMessages.length;
while ( numMessages-- ) {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent( 'click', true, true );
unreadMessages[numMessages].dispatchEvent(clickEvent);
}
Here's my full script that you can run inside your Chrome console (or turn it into an extension/userscript) to change the default behavior of the checkbox from selecting ALL messages to just the unread messages only:
var hasUILoaded = setInterval( function() {
if( document.getElementById('canvas_frame').contentDocument.getElementsByClassName('J-Zh-I J-J5-Ji J-Pm-I L3') ) {
clearInterval( hasUILoaded );
setTimeout( function() {
var content_frame = document.getElementById('canvas_frame').contentDocument;
var chkbox = content_frame.getElementsByClassName( 'J-Zh-I J-J5-Ji J-Pm-I L3' );
var chkbox = chkbox[0].childNodes[0];
var unreadMessages = content_frame.getElementsByClassName('zE'); // DOM structure: <tr class=zE> <td> <img><input> </td> </tr> so we add ".childNodes[0].childNodes[1]" whenever we want to access the check boxes of each message.
var allMessages = content_frame.getElementsByClassName('zA');
chkbox.onclick = function() {
if( chkbox.checked ) {
var numUnread = unreadMessages.length;;
var numAll = allMessages.length;
setTimeout(function () {
if( allMessages[0].childNodes[0].childNodes[1].checked ) {
while ( numAll-- ) {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent( 'click', true, true );
allMessages[numAll].childNodes[0].childNodes[1].dispatchEvent(clickEvent);
}
}
}, 10);
setTimeout(function (){
if(!unreadMessages[0].childNodes[0].childNodes[1].checked) {
while ( numUnread-- ) {
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent( 'click', true, true );
unreadMessages[numUnread].childNodes[0].childNodes[1].dispatchEvent(clickEvent);
}
}
}, 30);
}
}
}, 100);
}
}, 300);

Categories