Capture different keys in 2 separate tampermonkey scripts - javascript

I can't figure out what I'm missing. I have 2 separate Tampermonkey scripts which run on every page that tries to capture their associated keystroke. Each script will run different processes with their single keystroke. I realized with both of them enabled, it only activates the second one listed in the dashboard. How can I keep the 2 scripts separated while being able to have both active on all the pages.
For example these 2 separate scripts:
Script 1
// ==UserScript==
// #name Ctrl Y Function
// #namespace http://tampermonkey.net/
// #version 0.1
// #description testing key stroke function
// #author You
// #match https://*/*
// #grant none
// ==/UserScript==
(function() {
'use strict';
window.onkeyup = function(e){
if (event.key == "y" && event.ctrlKey){
console.log("Ctrl + Y pressed!")
} //if (event.key == "y" && event.ctrlKey)
} //window.onkeyup = function(e)
})(); //Main
Script 2
// ==UserScript==
// #name Ctrl Q Function
// #namespace http://tampermonkey.net/
// #version 0.1
// #description testing key stroke function
// #author You
// #match https://*/*
// #grant none
// ==/UserScript==
(function() {
'use strict';
window.onkeyup = function(e){
if (event.key == "q" && event.ctrlKey){
console.log("Ctrl + q pressed!")
} //if (event.key == "q" && event.ctrlKey)
} //window.onkeyup = function(e)
})(); //Main

After digging around it seems it's not a problem with Tampermonkey but the way I implemented the key listener. The original way I tried seems to be replacing the onkeyup function but I really needed to add an extra listener.
Script 1
// ==UserScript==
// #name Ctrl Y Function
// #namespace http://tampermonkey.net/
// #version 0.1
// #description testing key stroke function
// #author You
// #match https://*/*
// #grant none
// ==/UserScript==
(function() {
'use strict';
document.addEventListener('keydown', (e) => {
if (event.key == "y" && event.ctrlKey){
console.log("Ctrl + Y pressed!")
} //if (event.key == "y" && event.ctrlKey)
}, false); //document.addEventListener('keydown', (event) =>
})(); //Main
Script 2
// ==UserScript==
// #name Ctrl Q Function
// #namespace http://tampermonkey.net/
// #version 0.1
// #description testing key stroke function
// #author You
// #match https://*/*
// #grant none
// ==/UserScript==
(function() {
'use strict';
document.addEventListener('keydown', (e) => {
if (event.key == "q" && event.ctrlKey){
console.log("Ctrl + q pressed!")
} //if (event.key == "q" && event.ctrlKey)
}, false); //document.addEventListener('keydown', (event) =>
})(); //Main

Related

Limit jQuery .keydown() to Only specific Keys

I am using WordPress jQuery to build some shortcuts for my site.
It works as intended, however it doesn't stop when other keys are pressed.
For example, if I press, f it performs the necessary task. But if I press CTRL + f then also it does the task.
Infact, I tried, other keys like f and p and that too worked.
I want it to only work on the specific key. Incase of any other key press, it should not run. It should run on clicking f, but NOT run on CTRL + f.
I tested this on Chrome 92.0.4515.159 and Chrome Beta Version 94.0.4606.31 on Windows 7 and 8.
jQuery(window).keydown(function(e) {
if( e.which === 27){
jQuery('.SearchCreationsClose').click();
}
if(e.key == "f"){
e.preventDefault();
jQuery('.SearchCreationsIcon').click();
}
});
I saw the answer from #diego and understand that you want to implement shortcuts such that they are limited to a specific key combination, extra keypress will stop the function.
The solution was really good, but reading comments there are 2 main problems, 1.) It stops when the window goes out of focus, and 2.) event.preventDefault doesn't not work
I have a solution, that works without the timeout function, making it solve the e.preventDefault problem. And I use a different approach to make it happen.
var MyKeys= 0;
jQuery(window).focus(function(){
MyKeys = 0;
});
jQuery(window).keyup(function(e){
if(MyKeys > 0){
MyKeys--
}
else {
MyKeys = 0;
}
});
jQuery(window).keydown(function(e){
if (!e.repeat) {
MyKeys++
}
if(e.which == 70 && MyKeys === 1){
e.preventDefault();
console.log('f is pressed')
}
if(e.which == 70 && (e.ctrlKey || e.metaKey) && MyKeys === 2){
e.preventDefault();
console.log('ctrl+f is pressed')
}
});
Pretty sure that it solves the prevent default problem..
And I think it will solve the Alt + Tab problem too... (Haven't tested but am confident)
It isn't perfect. There is only a small limitation, if a user comes focuses into your Windows with a key already pressed, he would have to lift the key before being able to use the shortcut. He cants just focus in with CTRL pressed and then press f.
I think the best solution would be to merge this: Can jQuery .keypress() detect more than one key at the same time? with some sort of timeout like this:
var keys = {};
var eventTimeout;
$(document).keydown(function (e) {
// Reset timeout
if (eventTimeout) {
clearTimeout(eventTimeout);
}
keys[e.which] = true;
eventTimeout = setTimeout(function () {
if (typeof keys[70] !== "undefined" && Object.keys(keys).length === 1) { // 70 is the "f" key and it is the only pressed key
e.preventDefault();
$('.SearchCreationsIcon').click();
}
}, 200); // Takes 200ms of time before going on
});
$(document).keyup(function (e) {
setTimeout(function(){
delete keys[e.which];
},210);
});
The idea is that when you detect keydown you wait a little bit to see if there are others key pressed or just the "f" key. The keyup clears the buffer to ensure there are no leftovers
Edit for combo:
This would be the solution to catch CTRL + SHIFT + S, see updated code. I just moved out code from if and nested into separated function. You could easily abstract the function to accept an arbitrary number of keys simultaneously pressed together with which keys should be pressed and create a single function. You got the idea
var keys = {};
var eventTimeout;
$(document).keydown(function (e) {
// Reset timeout
if (eventTimeout) {
clearTimeout(eventTimeout);
}
keys[e.which] = true;
eventTimeout = setTimeout(function () {
if (isFPressed() || isMyComboPressed()) { // 70 is the "f" key and it is the only pressed key
e.preventDefault();
$('.SearchCreationsIcon').click();
}
}, 200); // Takes 200ms of time before going on
});
function isFPressed() {
return typeof keys[70] !== "undefined" && Object.keys(keys).length === 1;
}
/**
* 16 - Key code of SHIFT
* 17 - Key code of CTRL
* 83 - Key code of "s"
* #returns {boolean}
*/
function isMyComboPressed(){
return typeof keys[16] !== "undefined" && keys[17] !== "undefined" && keys[83] !== "undefined" && Object.keys(keys).length === 3;
}
$(document).keyup(function (e) {
setTimeout(function () {
delete keys[e.which];
}, 210);
});
Use e.ctrlKey which is true if ctrl key was pressed:
jQuery(window).keydown(function(e) {
if(e.key=='f' && e.ctrlKey){
console.log('ctrl+f');}
else if (e.key=='f'){
console.log('"JUST" f key');
}
});
If you want to catch only f keydown but not ctrl+f:
jQuery(window).keydown(function(e) {
if(e.key=='f' && !e.ctrlKey){
console.log('"JUST" f key');
}
});

addEventListener not handling keys right in old Chrome version

I'm trying to add an event listener to a web-site via a userscript like this:
// ==UserScript==
// #name Reuters: j/k navigation
// #version 0.01
// #match https://www.reuters.com/*
// #run-at document-start
// #grant none
// ==/UserScript==
addEventListener('keypress', e => {
if (e.ctrlKey ||
e.altKey ||
e.metaKey ||
e.shiftKey ||
(e.key !== 'k' && e.key !== 'j')) {
alert("NO");
return;
}
alert("YES");
});
And while in Firefox it indeed triggers the right alerts dependent on keys a user presses, in Chrome for some reason all I get is always "NO", whether Space, j, k, l or n, for example, are pressed.
What could be the problem here? Thanks.
(I'm limited to an old OSX at the moment, so both Firefox and Chrome are quite old -- Chrome is 49 -- but I doubt this should be an issue...)
The .key property was not available in Chrome 49; use .which.
Like so:
const jKey = 'j'.charCodeAt (0);
const kKey = 'k'.charCodeAt (0);
addEventListener ('keypress', e => {
if (e.ctrlKey ||
e.altKey ||
e.metaKey ||
e.shiftKey ||
(e.which !== jKey && e.which !== kKey) ) {
console.log (e.which, "NO");
return;
}
console.log (e.which, "YES");
} );
Click here then press keys...

Sending an arrow key event to the window via Greasemonkey

I'm trying to make a simple script to re-map the WASD keys to send up/left/down/right key events to the window. I'm using the latest Firefox version and Greasemonkey.
This is the script I have right now, and I've tried numerous variations of it, plus some other methods I found online. Nothing works.
// ==UserScript==
//
// #grant unsafeWindow
//
// ==/UserScript==
function KeyCheck(e)
{
//alert(e.keyCode);
var key = 0;
if (e.keyCode == 87)
{
key = 38;
}
else if (e.keyCode == 65)
{
key = 37;
}
else if (e.keyCode == 83)
{
key = 40;
}
else if (e.keyCode == 68)
{
key = 39;
}
var ev = document.createEvent ('KeyboardEvent');
ev.initKeyEvent('keypress', true, true, window, false, false, false, false, key, key);
unsafeWindow.dispatchEvent(ev);
}
unsafeWindow.addEventListener('keydown', KeyCheck, true);
All I need is one simple function that can successfully post a key event to the window or document. Manually scrolling the page is not an option here (because I want to make sure that other scripts that may be running on the page that detect the arrow keys are triggered)
OK -- I figured out a way to pull it off.
Tested under Firefox and GreaseMonkey -- I didn't test it under Chrome.
Edit Tested under Chrome and TamperMonkey. Keypresses were registered as well. I don't believe the regular built-in Chrome user-scripts have unsafeWindow and they are much more limited.
In short, the code won't work as user-script.
So, we cheat the system and turn the user script into a regular script and inject it into the page itself.
Now, it doesn't actually work and scroll -- but it does catch the key events and throws up the alert.
EDIT: NOTES
Its been my experience that GreaseMonkey is pretty much paranoid about letting anything accidentally slip from the unsafe window into the very sandboxed and protected world of user-scripts.
Events, in particular, are kind of tricky. An event registered to unsafeWindow cannot and will not bubble back up to GreaseMonkey -- something which has caused me no small amount of grief.
// ==UserScript==
//
// #grant unsafeWindow
// #include http://*/*
// #include https://*/*
//
// ==/UserScript==
var head = unsafeWindow.document.getElementsByTagName('head') [0],
script = unsafeWindow.document.createElement('script');
// The magic is here.
// We turn the script into a string which will get parsed be the JS compiler
// when it is added to the unsafeWindow.document.
var scriptTxt = '(' + fn + ')()';
script[script.innerText ? 'innerText' : 'textContent'] = scriptTxt;
head.appendChild(script);
// This is just a wrapper to hold things.
function fn() {
function KeyCheck(e)
{
alert(e.keyCode);
var key = 0;
if (e.keyCode == 87)
{
key = 38;
}
else if (e.keyCode == 65)
{
key = 37;
}
else if (e.keyCode == 83)
{
key = 40;
}
else if (e.keyCode == 68)
{
key = 39;
}
var ev = document.createEvent('KeyboardEvent');
ev.initKeyEvent('keypress', true, true, window, false, false, false, false, key, key);
window.dispatchEvent(ev);
}
window.addEventListener('keydown', KeyCheck, true);
};

Pause Javascript timer when the tab is not in focus

I have a requirement to show real time update of the number of people who did some action.
I implemented this functionality by making an ajax request to the server every 20 seconds.
But this ajax request happens even if the tab is not in focus/no one is looking at the update. Is there a way to figure out if the tab is active?
I have the following code(simplified version) and it doesn't work.
timer = undefined
$(document).ready ->
timedCountUpdate()
window.top.onblur = ->
clearTimeout(timer)
window.top.onfocus = ->
timer = timedCountUpdate()
#timedCountUpdate = () ->
timer = setTimeout(updateCountIndicator, 20000)
#updateCountIndicator = () ->
$('.indicator').html = 100
timedCountUpdate()
I still see the call being made every 20s even if i am not in the tab that has the app loaded. I am testing in chrome.
In Coffeescript w/ jquery:
$ ->
timeout_id = null
resumeTimer = () ->
# make ajax call here
# Prevent multiple timers from operating simultaneously:
clearTimeout timeout_id if timeout_id?
# Recursive step (ideally fires in 'success' handler of ajax call)
timeout_id = setTimeout(resumeTimer, 2000)
$(window.top).focus () =>
resumeTimer()
$(window.top).blur () =>
clearTimeout timeout_id
# Start timer immediately:
resumeTimer()
I know this is an old question, but I stumbled upon it in a Google search and wanted to provide another alternative that's better suited for what you're wanting to do.
The Page Visibility API is how these types of things should be done moving forward (or now IE10+). The API provides a visibilityChange event that triggers when the visibility of the tab changes. In the callback, checking the document.hidden property will tell you whether the tab is hidden or not.
From there, clear your interval or start it back up again.
In your case, i would do something like :
var tab_paused = false; // global
if (typeof window.top.onblur === 'function')
{
window.top.onblur = function() {
tab_paused = true;
};
}
if (typeof window.top.onfocus === 'function')
{
window.top.onfocus = function() {
tab_paused = false;
};
}
if (typeof document.onfocusout === 'function')
{
document.onfocusin = function() {
tab_paused = true;
};
}
if (typeof document.onfocusin === 'function')
{
document.onfocusin = function() {
tab_paused = false;
};
}
var ctx = setInterval(function() {
if (tab_paused === false)
{
$('.indicator').html(100);
}
}, 100);

replace jquery's run only after DOM is loaded functionality

I am trying to remove dependence on jQuery. I have stolen a couple of functions I needed from underscore.js, but still need my code to run after the DOM is loaded in the same way as jQuery
Original
$(function(){
// all my code
})
Desired
var afterload = function(content){
// what goes here..?
}
afterload(function(){
// my code that should run after the DOM is loaded
})
There is a nice solution from https://github.com/freelancephp/DOMReady,
Here is a script
/**
* DOMReady
*
* #fileOverview
* Cross browser object to attach functions that will be called
* immediatly when the DOM is ready.
* Released under MIT license.
* #version 2.0.0
* #author Victor Villaverde Laan
* #link http://www.freelancephp.net/domready-javascript-object-cross-browser/
* #link https://github.com/freelancephp/DOMReady
*/
/**
* #namespace DOMReady
*/
var DOMReady = (function () {
// Private vars
var fns = [],
isReady = false,
errorHandler = null,
run = function ( fn, args ) {
try {
// call function
fn.apply( this, args || [] );
} catch( err ) {
// error occured while executing function
if ( errorHandler )
errorHandler.call( this, err );
}
},
ready = function () {
isReady = true;
// call all registered functions
for ( var x = 0; x < fns.length; x++ )
run( fns[x].fn, fns[x].args || [] );
// clear handlers
fns = [];
};
/**
* Set error handler
* #static
* #param {Function} fn
* #return {DOMReady} For chaining
*/
this.setOnError = function ( fn ) {
errorHandler = fn;
// return this for chaining
return this;
};
/**
* Add code or function to execute when the DOM is ready
* #static
* #param {Function} fn
* #param {Array} args Arguments will be passed on when calling function
* #return {DOMReady} For chaining
*/
this.add = function ( fn, args ) {
// call imediately when DOM is already ready
if ( isReady ) {
run( fn, args );
} else {
// add to the list
fns[fns.length] = {
fn: fn,
args: args
};
}
// return this for chaining
return this;
};
// for all browsers except IE
if ( window.addEventListener ) {
document.addEventListener( 'DOMContentLoaded', function(){ ready(); }, false );
} else {
// for IE
// code taken from http://ajaxian.com/archives/iecontentloaded-yet-another-domcontentloaded
(function(){
// check IE's proprietary DOM members
if ( ! document.uniqueID && document.expando ) return;
// you can create any tagName, even customTag like <document :ready />
var tempNode = document.createElement( 'document:ready' );
try {
// see if it throws errors until after ondocumentready
tempNode.doScroll( 'left' );
// call ready
ready();
} catch ( err ) {
setTimeout( arguments.callee, 0 );
}
})();
}
return this;
})();
and you can use like this
DOMReady.add(function (){
alert( 'DOM is ready!' );
});
I will leave my question here, because I asked after searching stack overflow for answers, but searched the wring terms I guess. I hope other people will find this answer useful.
Initially, I was intending a functionally equivalent to jQuery solution, but without the jQuery dependance. Turns out that is very large and complex...
https://stackoverflow.com/a/7053197/665261
I then decided I wanted a concise solution, because the reason for removing jQuery dependence was to make my code load faster on low spec devices.
I am only targeting Android mobile devices, I ended up using this:
function afterload(content){
/in/.test(document.readyState)
? setTimeout(function(){ afterload(content) }, 9)
: content()
}
It should work on desktop browsers too, apparently tested on all major ones. Details and a fix for FF<3.6 can be found here: http://dustindiaz.com/smallest-domready-ever

Categories

Resources