There's been a recent trend to use spacebar as a meta key while performing certain drag actions in more graphical apps.
The problem is that the mouse flickers while holding spacebar (or any non-modifier key) down and moving your mouse: http://jsfiddle.net/S3AJr/4/
Example code:
$(function() {
var count = 1,
isSpaceBarPressed = false;
$('body').on('keydown', function(e) {
e.preventDefault();
if (e.which === 32) {
isSpaceBarPressed = true;
}
});
$('body').on('keyup', function(e) {
e.preventDefault();
if (e.which === 32) {
isSpaceBarPressed = false;
}
});
$('body').on('mousemove', function(e) {
if (isSpaceBarPressed) {
e.preventDefault();
$('.pizza').text(count++);
}
});
});
Is there a way to fix this or am I limited to ctrl, alt, shift and meta?
Easy fix bro!
// note include underscore-min.js for our throttle function
$(function () {
var count = 1,
isSpaceBarPressed = false;
var throttledRender = _.throttle(function () {
$('.pizza').text(count);
}, 200);
$('body').on('keydown', function (e) {
e.preventDefault();
if (e.which === 32) {
isSpaceBarPressed = true;
}
});
$('body').on('keyup', function (e) {
e.preventDefault();
if (e.which === 32) {
isSpaceBarPressed = false;
}
});
$('body').on('mousemove', function (e) {
if (isSpaceBarPressed) {
e.preventDefault();
count++;
throttledRender();
}
});
});
The problem is that you are rendering WAY too often (every time the event fires). Instead, you want to increment your variable every time but only render periodically. See this forked fiddle using underscore's throttle function. If you prefer, there is a jQuery plugin for this too.
Related
I have code for 3 different task which I want to execute by clicking and pressing a key, so there will be 3 different combination of clicking and pressing. For example-
window.addEventListener("keydown", function(e){
if(e.keyCode === 16) {console.log('Yap! Shift works...');}
if(e.keyCode === 17) {console.log('Yap! Ctrl works...');
document.addEventListener('click',function (event) {
console.log(event.target.className);
}, false);
}
},false);
Now, when I press click shift key, I get related output, when I click Ctrl key and then click, I get the class name of the object I click on.
But the problem is, the output keeps coming as much I hold the key!! I want to execute the part of my code for once, and exactly when the key is pressed and a clicked is occurred.
How can I do that?
In general, how can I execute 3 part of code for three different tasks by clicking and pressing efficiently?
Adding an event handler while handing an event, is often the wrong way to solve a problem. Imagine how you will accumulate adding handlers... in your case there will eventually be many bindings to the same click handler.
It is better to bind the handlers you need immediately, and then work with keeping state on what exactly needs to happen while handling the event.
In these key handlers (keydown, keyup), keep track of whether the Shift/Control keys are depressed or not.
Also, use e.key as e.keyCode is deprecated.
Here is how that could work:
let keys = {
"Shift": false,
"Control": false
};
function keyToggle(e) {
if (!(e.key in keys)) return; // not ctrl or shift
let isKeyDown = e.type === "keydown";
if (isKeyDown === keys[e.key]) return; // key position did not change
keys[e.key] = isKeyDown;
console.log(e.key + (isKeyDown ? " pressed" : " released"));
}
document.addEventListener("keydown", keyToggle, false);
document.addEventListener("keyup", keyToggle, false);
document.addEventListener('click', function (e) {
if (keys["Control"]) console.log(event.target.className);
}, false);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
As you addEventListener you can also removeEventListener.
For that you need a reference to your event handler, so you cannot use anonymous functions, but named functions or functions stored in a variable.
Edit
Here is an example of using CTRL+click:
// CTRL + CLICK implementation
let hasCtrl = false;
// Store the handler in a constant or variable
const handleClick = function(event) {
console.log(event.target.className);
}
// Use named function
function handleKeyDown(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
}
const setCtrlInactive = (e) => {
if (!hasCtrl && e.keyCode === 17) {
console.log('Nope! Ctrl does not work...');
document.removeEventListener('click', handleClick);
hasCtrl = true;
}
}
const setCtrlActive = (e) => {
if (hasCtrl && e.keyCode === 17) {
console.log('Yap! Ctrl works...');
document.addEventListener('click', handleClick);
hasCtrl = false;
}
}
document.addEventListener("keyup", setCtrlInactive);
document.addEventListener("keydown", setCtrlActive);
document.addEventListener("keydown", handleKeyDown);
<main class="main">Main</main>
<aside class="aside">Aside</aside>
Well you can easily create an variable to lock it:
var locked = false;
window.addEventListener("keydown", function(e){
if(e.keyCode === 16 && !locked) {console.log('Yap! Shift works...'); locked =
true;}
if(e.keyCode === 17 && !locked ) {console.log('Yap! Ctrl works...');
locked = true;
}
},false);
document.addEventListener('click',function (event) {
if(locked){
// do something
console.log(event.target.className);
}
}, false);
window.addEventListener("keyup", function(){
locked = false;
}
That's because you called addEventListener('click') in the keydown event handler.
let ctrl = false;
window.addEventListener("keydown", function(e) {
if (e.keyCode === 16) {
console.log('Yap! Shift works...');
}
if (e.keyCode === 17 && ctrl === false) {
console.log('Yap! Ctrl works...');
ctrl = true;
}
});
window.addEventListener("keyup", function(e) {
if (e.keyCode === 17) {
ctrl = false;
}
});
document.addEventListener('click', function(event) {
if (ctrl) {
console.log(event.target.className);
}
}, false);
Instead, you should use keyup event and flag variable.
In this random English-Wikipedia edit page one can add some content (say "test"), then saving it by the preexisting key-combo of Alt+Shift+S.
I desire to prevent this behavior specifically (without removing the save button with document.querySelector("#wpSave").remove();).
I tried the following code that failed:
// ==UserScript==
// #name wiki
// #match https://*.wikipedia.org/*
// ==/UserScript==
document.addEventListener("DOMContentLoaded", ()=>{
document.addEventListener('keypress', function(e) {
if (e.key == 16 && e.key == 18 && e.key == 83) {
return false;
}
});
});
I also tried replacing return false with e.preventDefault() or evt.stopPropagation(), but all failed (no console errors).
What's wrong with the code?
Note: This question differs from this one by focusing on disabling a given preexisting key-combo functionality in general, and not on saving functionalities in general.
Update for dotoconor
I used this in console but I still have the same problem:
document.addEventListener("DOMContentLoaded", ()=>{
const state = {};
document.addEventListener('keydown', function(e) {
state[e.key] = true;
});
document.addEventListener('keyup', function(e) {
state[e.key] = false;
});
document.addEventListener('keyup', function(e) {
state[e.key] = false;
if (state["Alt"] && state["Shift"] && (state["S"] || state["s"])) {
return e.preventDefault();
}
});
});
Only one key event will be present at a time, so you have to create a state machine to determine which ones are on and off.
Consider this:
const state = {};
document.addEventListener('keydown', function(e) {
state[e.key] = true;
});
document.addEventListener('keyup', function(e) {
state[e.key] = false;
});
Now with this, you can check if your desired keys are all being pushed down at one time, then prevent the last keypress from trickling down the DOM.
document.addEventListener('keyup', function(e) {
state[e.key] = false;
if (state["Alt"] && state["Shift"] && (state["S"] || state["s"])) {
return e.preventDefault();
}
});
I have a header and a footer of an html invoice containing contact information. Whatever content changes I make in the header I want to show them in the footer and vice versa.
For example if I change the phone number in the header, it should change in the footer once I click out of the text box. I've only managed it to work in one and on enter unfortunately:
$(document).ready(function () {
function getValue() {
var x;
x = document.getElementById("phone-number").value;
document.getElementById("phone-number-footer").value = x;
}
$('#phone-number').keypress(function (e) {
if (e.which == 13) { //Enter key pressed
getTextBoxValue();
}
});
});
Javascript function does not work inside document.ready function. It should moved below or above document.ready function.
So please check below solution.
$(document).ready(function() {
$(document).on('blur', '#phone-number', function(e) {
getHeaderTextBoxValue();
});
$(document).on('keypress', '#phone-number', function(e) {
if (e.which === 13) {
getHeaderTextBoxValue();
}
});
$(document).on('blur', '#phone-number-footer', function(e) {
getFooterTextBoxValue();
});
$(document).on('keypress', '#phone-number-footer', function(e) {
if (e.which === 13) {
getFooterTextBoxValue();
}
});
});
function getHeaderTextBoxValue() {
var x;
x = document.getElementById("phone-number").value;
document.getElementById("phone-number-footer").value = x;
}
function getFooterTextBoxValue() {
var x;
x = document.getElementById("phone-number-footer").value;
document.getElementById("phone-number").value = x;
}
I can toogle the ctrlBtn["pressed"] to true when I press CTRL or ALT key on Mac. (inspecting by console.log)
However, when I trigger the mouse up it couldn't enter the saveWord(selected). ctrlBtn["pressed"] still stays false.
What a good practice to catch the keypress and mouse up at the same time.
I'm writing a chrome extension currently. The whole script is here! https://gist.github.com/weichenghsu/4bbb2d1b6c3d34d55a942c3c6713fa89
Thanks!
window.addEventListener('load', function () {
...
document.body.addEventListener('mouseup', function (e) {
var selection = window.getSelection();
var selected = selection.toString().trim();
if (selected) {
console.log(ctrlBtn);
console.log(ctrlBtn["pressed"]);
if (ctrlBtn["pressed"] == true) {
saveWord(selected);
}
updateHighlighter();
}
});
window.addEventListener('keyup', function (e) {
ctrlBtn["pressed"] = false;
});
window.addEventListener('keydown', function (e) {
console.log(e.keyCode);
if (e.keyCode == 17 || e.keyCode == 18) {
ctrlBtn["pressed"] = true;
}
e.preventDefault();
return false;
});
I have created two functions. To keep it simple lets take for an example the following:
I got functions firing different events for the same objects. You can activate them using your keyboard arrows
$("body").keydown(function(e) {
if (event.which == 39) open_second_layer();
});
$("body").keydown(function(e) {
if (event.which == 37) open_first_layer();
});
As soon as I have fired one function and press the same key again it fires the animation one more time (unnecessarily).
Because of that as soon as the function open_second_layer has been fired, it should not be able to be fired again, until open_first_layer is fired again. The same should be the case the other way round.
I found .bind and .when as possible solutions, but can't figure out how to use them the right way for that case. I appreciate every suggestions or keywords to google.
You can keep a state variable and track when changes are made to it:
var state_changed = (function() {
var current = null;
return function(state) {
if (state == current) {
return false;
}
current = state;
return true;
};
}());
function open_first_layer()
{
if (!state_changed(1)) {
return;
}
// rest of code
}
function open_second_layer()
{
if (!state_changed(2)) {
return;
}
// rest of code
}
$("body").keydown(function(e) {
if (event.which == 39) {
open_second_layer();
} else if (event.which == 37) {
open_first_layer();
}
});
You can use jQuery's one().
In your first click handler, you bind the second one.
In your second click handler, you bind the first one.
sample
<div id=activate-first>first</div>
<div id=activate-second style="display:none;">second</div>
$(document).ready(function () {
function slide_first(){
$('#activate-first').show();
$('#activate-second').hide();
$('#activate-second').one('click', slide_first);
};
function slide_second(){
$('#activate-first').hide();
$('#activate-second').show();
$('#activate-first').one('click', slide_second);
};
$('#activate-first').one('click', slide_second);
$('#activate-second').one('click', slide_first);
});
Put the other function inside slide_first, like:
function slide_first(){
// other code
$('#activate_second').one('click', slide_second);
}
$('#activate_first').one('click', slide_first);
or use an Anonymous function to do the same:
$('#activate_first').one('click', function(){
// slide_first code here
$('#activate_second').one('click', function(){
// slide_second code here
});
});
Maybe your really want:
function recursiveSlider(){
$('#activate_first').one('click', function(){
// slide_first code here
$('#activate_second').one('click', function(){
// slide_second code here
recursiveSlider();
});
});
}
recursiveSlider();
This is a perfect use case for delegation. You have a single click event, and whenever the event happens, you determine what has been clicked, and you take action accordingly:
$(document.body).on("click", function(ev) {
var $targ = $(ev.target);
if ($targ.is('#button_1')) {
// someone clicked #button_1
}
if ($targ.is('.page-2 *')) {
// something inside of .page-2 was clicked!!
}
});
UPDATE: now the OP has included more code, I'm not sure the issue is - there's no need to bind and unbind events...
http://jsfiddle.net/ryanwheale/uh63rzbp/1/
function open_first_layer() {
$('#first_panel').addClass('active');
$('#second_panel').removeClass('active');
}
function open_second_layer() {
$('#first_panel').removeClass('active');
$('#second_panel').addClass('active');
}
// one event === good
$("body").keydown(function(e) {
if (event.which == 39) open_second_layer();
if (event.which == 37) open_first_layer();
});
... or if you're trying to build a slider, I suggest changing your naming convention:
http://jsfiddle.net/ryanwheale/uh63rzbp/2/
var current_layer = 1,
$all_layers = $('[id^="panel_"]'),
total_layers = $all_layers.length;
function move_layer (dir) {
current_layer += dir;
if (current_layer < 1) current_layer = total_layers;
else if (current_layer > total_layers) current_layer = 1;
$all_layers.removeClass('active');
$('#panel_' + current_layer).addClass('active');
}
// one event === good
$("body").keydown(function(e) {
if (event.which == 39) move_layer(1);
if (event.which == 37) move_layer(-1);
});
move_layer(0);