Kiểm tra xem phím có bị sập nguồn không?


91

Có cách nào để phát hiện xem một khóa hiện đang bị lỗi trong JavaScript không?

Tôi biết về sự kiện "keydown", nhưng đó không phải là điều tôi cần. Một lúc nào đó SAU KHI nhấn phím, tôi muốn phát hiện xem nó có còn được nhấn xuống hay không.

PS Vấn đề lớn nhất dường như là sau một khoảng thời gian, phím bắt đầu lặp lại, gây ra các sự kiện keydown và keyup như một con quái vật. Hy vọng rằng chỉ có một hàm isKeyDown (phím) đơn giản, nhưng nếu không có thì vấn đề này sẽ cần được khắc phục / khắc phục.


6
Một vấn đề phổ biến với các câu trả lời mà tôi thấy ở đây là nếu bạn nhấn giữ một phím, sau đó thay đổi tab hoặc thay đổi tiêu điểm, để phím lên và sau đó chuyển trở lại, mã sẽ tin rằng phím đang xuống cho đến khi bạn nhấn lại hoặc di chuyển di chuột qua trang. :-(
Eric Mickelsen 13/10/16

Câu trả lời:


71

Có cách nào để phát hiện xem một khóa hiện đang bị lỗi trong JavaScript không?

Không. Khả năng duy nhất là theo dõi từng keyupkeydownvà ghi nhớ.

sau một khoảng thời gian, khóa bắt đầu lặp lại, kích hoạt các sự kiện keydown và keyup giống như một con quái vật.

Nó không nên. Bạn chắc chắn sẽ bị keypresslặp lại và trong nhiều trình duyệt, bạn cũng sẽ bị lặp lại keydown, nhưng nếu keyuplặp lại, đó là một lỗi.

Thật không may, nó không phải là một lỗi hoàn toàn chưa từng có: trên Linux, Chromium và Firefox (khi nó đang được chạy dưới GTK +, trong các bản phân phối phổ biến như Ubuntu), cả hai đều tạo ra các chuỗi keyup-key-keydown lặp lại cho các phím được giữ, không thể phân biệt được với ai đó đang gõ phím thật nhanh.


14
Bạn, quý ngài, một quý ông, và một học giả. Chromium và Firefox trên Ubuntu là môi trường phát triển chính của tôi, vì vậy điều đó giải thích chính xác vấn đề mà tôi đang gặp phải. Hy vọng rằng nó sẽ trở nên tốt hơn, nếu không thì giải pháp hack bộ đếm thời gian đó có thể là cách giải quyết duy nhất.
Daniel X Moore

3
Vâng, thật là thất vọng khi không có tiến bộ trong việc này. Xem bug.launchpad.net/ubuntu/+bug/369880 . Tôi đang viết một trò chơi trên trình duyệt và cách giải quyết của tôi hiện tại là bám vào các phím bổ trợ (shift, ctrl, v.v.) mà không lặp lại.
bobince

2
Không, nó có thể phân biệt chúng từ keypresses lặp đi lặp lại chính hãng bởi họ thiếu tương ứng với keyupcác sự kiện.
mako

4
8 năm sau vẫn vậy? Câu trả lời này có thể cần được cập nhật.
Marco Lavagnino

3
trợ giúp phân tích cú pháp: (Linux && (Chromium || (Firefox && GTK +)))
bobince

134

Ngoài việc sử dụng keyupkeydownthính giả theo dõi khi là chìa khóa đi xuống và sao lưu, có được thực sự một số tài sản mà cho bạn biết nếu phím nào đó là xuống.

window.onmousemove = function (e) {
  if (!e) e = window.event;
  if (e.shiftKey) {/*shift is down*/}
  if (e.altKey) {/*alt is down*/}
  if (e.ctrlKey) {/*ctrl is down*/}
  if (e.metaKey) {/*cmd is down*/}
}

Này có sẵn trên tất cả các trình duyệt được tạo ra đối tượng sự kiện, chẳng hạn như những từ keydown, keyupkeypress, vì vậy bạn không cần phải sử dụng MouseMove.

Tôi đã thử tạo các đối tượng sự kiện của riêng mình với document.createEvent('KeyboardEvent')document.createEvent('KeyboardEvent')và tìm kiếm e.shiftKeyvà tương tự, nhưng tôi không gặp may.

Tôi đang sử dụng Chrome 17 trên Mac


Tôi đang sử dụng cái này, trong cả trình duyệt mới và cũ, thậm chí cả của HTA
Jakob Sternberg

1
Có cách nào để thực hiện câu trả lời này khi một số hiện đang giảm không? Tôi đã tryed với if (e.keyCode == 49) {console.log ( "1 là xuống");} nhưng không làm việc :(
Roberto Sepúlveda Bravo

Này @ RobertoSepúlvedaBravo Robert dường như sẽ trả lời bạn câu hỏi trong câu trả lời tiếp theo. ;)
Mark Odey

Trên thực tế, bạn không nên tìm kiếm e.shiftKey trên keyup nếu bạn muốn dùng Shift. Sử dụng e.shiftKey trên ví dụ như onclick để thay thế.
maciek

45

Giải pháp của tôi:

var pressedKeys = {};
window.onkeyup = function(e) { pressedKeys[e.keyCode] = false; }
window.onkeydown = function(e) { pressedKeys[e.keyCode] = true; }

Bây giờ tôi có thể kiểm tra xem có phím nào được nhấn ở bất kỳ vị trí nào khác trong tập lệnh hay không bằng cách kiểm tra

pressedKeys["code of the key"]

Nếu đúng, phím được nhấn.


2
Vì các khóa đã là một hàm nên một tên biến khác có thể tốt hơn.
Raven

1
điều này sẽ không hoạt động để phát hiện xem phím Alt có bị sập hay không trong mọi trường hợp.
Michael

1
Này là rất tốt cho việc phát hiện lên / xuống / trái / phải
Jonathan Spiller

11

Tôi không tin rằng có bất kỳ thứ gì giống như hàm isKeyDown, nhưng bạn có thể viết cho riêng mình.

Về cơ bản, tạo một mảng có độ dài là số khóa bạn muốn theo dõi. Sau đó, sử dụng các tài liệu / trang / sự kiện điều khiển keyUp và keyDown, cập nhật mảng với trạng thái của khóa đó.

Sau đó, viết một hàm để kiểm tra xem một khóa nào đó bị hỏng và trả về bool.

var keyEnum = { W_Key:0, A_Key:1, S_Key:2, D_Key:3 };
var keyArray = new Array(4);

function onKeyDown()
{
    // Detect which key was pressed
    if( key == 'w' )
        keyArray[keyEnum.W_Key] = true;
    // Repeat for each key you care about...
}

function onKeyUp()
{
    // Detect which key was released
    if( key == 'w' )
        keyArray[keyEnum.W_Key] = false;
    // Repeat for each key you care about...
}

function isKeyDown(key)
{
    return keyArray[key];
}

Điều đó sẽ đạt được những gì bạn muốn.


1
Điều này là tốt và thực sự là một phần của giải pháp, nhưng nó không giải quyết được lỗi lặp lại keyup mà tôi đang gặp phải. Xem câu trả lời của bobince.
Daniel X Moore

4
Đây không phải là một giải pháp tốt vì bạn sẽ viết ngày càng nhiều ifs. Một "keyList = {};" là một đối tượng chấp nhận "keyList [key] = true;" mà không cần enum hoặc giới hạn vì nó sử dụng chỉ mục / thuộc tính chuỗi và hoạt động cho tất cả các khóa.
SparK

5

Đã kết thúc ở đây để kiểm tra xem đã có nội dung nào đó trong trình duyệt chưa, nhưng có vẻ như chưa có. Đây là giải pháp của tôi (rất giống với câu trả lời của Robert):

"use strict";

const is_key_down = (() => {
    const state = {};

    window.addEventListener('keyup', (e) => state[e.key] = false);
    window.addEventListener('keydown', (e) => state[e.key] = true);

    return (key) => state.hasOwnProperty(key) && state[key] || false;
})();

Sau đó, bạn có thể kiểm tra xem một phím được nhấn bằng is_key_down('ArrowLeft').


1

Những người khác đã hỏi loại câu hỏi này trước đây (mặc dù tôi không thấy bất kỳ sự lừa đảo rõ ràng nào ở đây ngay bây giờ).

Tôi nghĩ câu trả lời là keydownsự kiện (và cặp song sinh của nó keyup) là tất cả thông tin bạn nhận được. Việc lặp lại được kết nối khá chắc chắn vào hệ điều hành và chương trình ứng dụng không có nhiều cơ hội để truy vấn BIOS về trạng thái thực của khóa.

Những gì bạn có thể làm và có lẽ phải làm nếu bạn cần làm cho điều này hoạt động, là khử nảy phím theo chương trình. Về cơ bản, bạn có thể đánh giá keydownkeyupbản thân nhưng bỏ qua một keyupsự kiện nếu nó diễn ra quá nhanh sau lần cuối cùng keydown... hoặc về cơ bản, bạn nên trì hoãn phản hồi của mình keyupđủ lâu để chắc chắn rằng không có keydownsự kiện nào khác xảy ra sau 0,25 giây keyup.

Điều này sẽ liên quan đến việc sử dụng hoạt động hẹn giờ và ghi lại thời gian mili giây cho các sự kiện trước đó. Tôi không thể nói đó là một giải pháp rất hấp dẫn, nhưng ...


2
Tôi đã lo lắng rằng nó có thể đến thế này.
Daniel X Moore

1
/*
Tracks what keys are currently down on the keyboard
*/

function keyboard_module(onUpdate){
    var kb = {};
    var unicode_mapping = {};
    document.onkeydown = function(e){
        var unicode=e.charCode? e.charCode : e.keyCode
        var key = getKey(unicode);
        kb[key] = true;
        if(onUpdate){
            onUpdate(kb);
        }
    }

    document.onkeyup = function(e){
        var unicode=e.charCode? e.charCode : e.keyCode
        var key = getKey(unicode);
        delete kb[key];
        if(onUpdate){
            onUpdate(kb);
        }
    }

    function getKey(unicode){
        if(unicode_mapping[unicode]){
            var key = unicode_mapping[unicode];
        }else{
            var key= unicode_mapping[unicode] = String.fromCharCode(unicode);
        }
        return key;
    }
    return kb;
}

function testing(kb){
    console.log('These are the down keys', kb);
}


var keyboard = keyboard_module(testing);

....
//somewhere else in the code
if(keyboard['K']){/*do something special */}

Không chắc chắn nếu String.fromCharCode (unicode); tra cứu nhanh hay không, đó là lý do tại sao tôi có đối tượng unicode_mapping. Đây có thể là thứ cần rút ra để cắt bớt mã này một chút nếu nó cực nhanh. Vì điều này sẽ được gọi nhiều lần cho các lần giảm phím, nên tốc độ rất quan trọng, do đó, tại sao tôi lại bi quan thêm ánh xạ.
RobKohr

1

Đoạn mã sau là những gì tôi đang sử dụng:

var altKeyDownCount = 0;
window.onkeydown = function (e) {
    if (!e) e = window.event;
    if (e.altKey) {
        altKeyDownCount++;
        if (30 < altKeyDownCount) {
            $('.key').removeClass('hidden');
            altKeyDownCount = 0;
        }
        return false;
    }
}

window.onkeyup = function (e) {
    if (!e) e = window.event;
    altKeyDownCount = 0;
    $('.key').addClass('hidden');
}

Khi người dùng giữ phím Alt trong một thời gian (khoảng 2 giây), một nhóm nhãn (class = 'key hidden') sẽ xuất hiện. Khi phím Alt được nhả ra, các nhãn sẽ biến mất. jQuery và Bootstrap đều được sử dụng.


và điều gì sẽ xảy ra nếu "Tab" được nhấn trong khi Alt bị ngắt?
Michael

1

Tôi biết đây là câu hỏi rất cũ, tuy nhiên có một thư viện JavaScript rất nhẹ (~ .5Kb) có thể "vá" hiệu quả việc kích hoạt không nhất quán của các trình xử lý sự kiện bàn phím khi sử dụng API DOM.

Thư viện là Keydrown .

Đây là mẫu mã tác vụ đã hoạt động tốt cho mục đích của tôi chỉ bằng cách thay đổi khóa để đặt trình nghe:

kd.P.down(function () {
  console.log('The "P" key is being held down!');
});

kd.P.up(function () {
  console.clear();
});

// This update loop is the heartbeat of Keydrown
kd.run(function () {
  kd.tick();
});

Tôi đã kết hợp Keydrown vào JavaScript phía máy khách của mình để có hoạt ảnh tạm dừng thích hợp trong trò chơi Đèn đỏ Đèn xanh mà tôi đang viết. Bạn có thể xem toàn bộ trò chơi tại đây . (Lưu ý: Nếu bạn đang đọc cái này trong tương lai, trò chơi phải hoàn chỉnh và có thể chơi được :-D!)

Tôi hi vọng cái này giúp được.


1

Tôi đã xem qua các câu trả lời ở trên và cách tiếp cận keydown/ đề xuất keyupchỉ hoạt động trong những trường hợp đặc biệt. Nếu người dùng nhấn phím tắt hoặc sử dụng cử chỉ phím để mở cửa sổ hoặc tab trình duyệt mới, thì a keydownsẽ được đăng ký, điều này tốt, vì tại thời điểm đó, không thể biết liệu khóa có phải là thứ mà ứng dụng web đang theo dõi hay không hoặc là một trình duyệt hoặc phím tắt hệ điều hành tiêu chuẩn. Quay lại trang trình duyệt, nó sẽ vẫn nghĩ rằng khóa được giữ, mặc dù nó đã được phát hành trong thời gian chờ đợi. Hoặc một số phím được giữ đơn giản trong khi người dùng đang chuyển sang tab hoặc ứng dụng khác bằng chuột, sau đó được phát hành ra bên ngoài trang của chúng tôi.

Các phím bổ trợ ( Shiftv.v.) có thể được giám sát thông qua, mousemovev.v. giả sử rằng có ít nhất một tương tác chuột được mong đợi khi tab quay lại, điều này thường xảy ra.

Đối với hầu hết tất cả các phím khác (trừ bổ, Tab, Delete, nhưng bao gồm Space, Enter), giám sát keypresssẽ làm việc cho hầu hết các ứng dụng - một chìa khóa giữ xuống sẽ tiếp tục cháy. Tuy nhiên, có một số độ trễ trong việc đặt lại khóa, do tính định kỳ của keypressviệc kích hoạt. Về cơ bản, nếu keypresskhông tiếp tục bắn, thì có thể loại trừ hầu hết các phím. Điều này, kết hợp với các công cụ sửa đổi khá kín gió, mặc dù tôi chưa khám phá những gì phải làm với TabBackspace.

Tôi chắc rằng có một thư viện nào đó ngoài kia tóm tắt về điểm yếu này của DOM, hoặc có thể một số thay đổi tiêu chuẩn DOM đã giải quyết vấn đề đó, vì đó là một câu hỏi khá cũ.


nhấn phím hiện không được dùng nữa. Nó dường như không hoạt động trên Chrome.
eadsjr

0

Nhìn vào câu trả lời này , và sử dụng onkeyuponkeydown. Đây là thông tin cụ thể hơn về những sự kiện đó.


1
Liên kết bạn trích dẫn là cho các sự kiện chuột, nơi tự động lặp lại không phải là vấn đề.
Carl Smotricz

phím lên và phím xuống sẽ không lặp lại. nhấn phím có thể.
Claudiu

tốt ngay cả khi họ làm, tôi đã tưởng tượng ra một cái gì đó giống như giải pháp TJMonk15, chỉ không muốn viết nó lên
Claudiu

1
Từ ngữ trên hai câu hỏi đó hoàn toàn giống nhau. Sự trùng hợp kỳ lạ, hay điều gì đó hơn thế nữa?
Mitch Lindgren

2
@Mitch: wow ... thật là khó tin. Tôi không biết tại sao ai đó lại tạo hai tài khoản để hỏi cùng một câu hỏi. hoặc nhiều khả năng người này chỉ sao chép câu hỏi kia và điều chỉnh nó cho các phím
Claudiu

0

Điều này hoạt động trên Firefox và Chrome.

Tôi có nhu cầu mở một tệp html đặc biệt cục bộ (bằng cách nhấn Enterkhi tệp được chọn trong trình khám phá tệp trong Windows), chỉ để xem tệp hoặc để chỉnh sửa tệp trong trình chỉnh sửa trực tuyến đặc biệt.

Vì vậy, tôi muốn phân biệt giữa hai tùy chọn này bằng cách giữ phím Ctrl-key hoặc không, trong khi nhấn Enter.

Như tất cả các bạn đã hiểu từ tất cả các câu trả lời ở đây, điều này có vẻ là không thực sự khả thi, nhưng đây là một cách bắt chước hành vi này theo cách có thể chấp nhận được đối với tôi.

Cách hoạt động của nó là như thế này:

Nếu bạn giữ phím Ctrl-khi mở tệp thì sự kiện keydown sẽ không bao giờ kích hoạt trong mã javascript. Nhưng một sự kiện keyup sẽ kích hoạt (khi cuối cùng bạn nhả Ctrl-key). Mã ghi lại điều đó.

Mã cũng tắt keyevents (cả keyup và keydown) ngay khi một trong số chúng xảy ra. Vì vậy, nếu bạn nhấn phím- Ctrlsau khi tệp đã mở, sẽ không có gì xảy ra.

window.onkeyup = up;
window.onkeydown = down;
function up(e) {
  if (e.key === 'F5') return; // if you want this to work also on reload with F5.

  window.onkeyup = null;
  window.onkeyup = null;
  if (e.key === 'Control') {
    alert('Control key was released. You must have held it down while opening the file, so we will now load the file into the editor.');
  }         
}
function down() {
  window.onkeyup = null;
  window.onkeyup = null;
}

-3
$('#mytextbox').keydown(function (e) {
            if (e.keyCode == 13) {
                if (e.altKey) {
                    alert("alt is pressed");
                }
            }
 });

nếu bạn nhấn alt + enter, bạn sẽ thấy cảnh báo.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.