使用javascript捕获在android虚拟键盘上键入的键

Posted

技术标签:

【中文标题】使用javascript捕获在android虚拟键盘上键入的键【英文标题】:Capture keys typed on android virtual keyboard using javascript 【发布时间】:2015-08-24 22:43:27 【问题描述】:

我有一个带有文本区域的网页,我需要捕获用户键入的键(以便我可以用不同的 unicode 字符替换键入的键)。我目前的代码如下:

$("#myTextArea").bind('keypress', function(event) 

    var keyInput = event.which;
   // call other functions

);

以上代码适用于 PC 和 iPhone/Safari。但是,在 android(三星)平板电脑上使用 Chrome 时会失败。出于某种原因,当我在 android 虚拟(软)键盘上键入时,不会触发“keypress”事件。安卓版本是5.0.2。

如果我尝试使用 "keyUp" 或 "keyDown",它总是为所有字符返回 229(返回键、空格、退格等除外)。

即使 keyCode 始终为 229,文本区域仍会显示用户键入的正确字符。这意味着设备知道输入了哪个密钥,但不知何故我无法使用 javascript 处理此事件(和密钥代码)。

以下是我迄今为止尝试过的替代方案及其结果:

$("#mainTextArea").on("keydown keyup", function(event)  
    // event.which and event.keyCode both return 229

$(document).on('keypress', function(event)  
    // function is not triggered

$('#myTextArea').bind('input keypress', function(event)  
   // comes inside function, but keyCode and which are undefined

感谢有关此问题的任何帮助..

【问题讨论】:

此处记录的 android 错误:bugs.chromium.org/p/chromium/issues/detail?id=118639 【参考方案1】:

不幸的是,您似乎在这里无能为力。 Keypress 事件已被弃用,因此不会被触发。 keyup 和 keydown 上的 229 表示键盘缓冲区正忙。原因——当你按下一个键——输入仍然不能保证是用户按下的,因为自动建议和其他事件可能会立即发生并使事件无效。 虽然在我看来,最好先发送密钥,然后在自动建议上触发另一个事件,以便您可以单独对其采取行动......

我目前知道的唯一一件事是附加到 keydown 和 keyup,将值存储在 keydown 上,获取 keyup 上的值并找到用户按下的增量。不幸的是,这不适用于非输入控件(例如 - 身体或类似的东西)。 也许不是你想听到的答案,但仍然如此。

【讨论】:

oninput 确实有效,但它没有相同的功能。至少在我的情况下,用 oninput 代替 keypress 真的很难。 不幸的是。不过,我不确定是否有更好的解决方案。 @PavelDonchev 请看我的回答【参考方案2】:

我遇到了同样的问题。有几种解释,但无论如何,没有提供解决方案似乎很奇怪。 目前我通过捕获oninput 事件解决了这个问题。

"这个事件和onchange事件类似,不同的是oninput事件是在元素的值发生变化后立即发生,而onchange是在元素失去焦点、内容发生变化后发生"

此事件也支持插入文本,来自粘贴文本或更正和建议。

它没有给我完美的解决方案,因为我只能在输入文本后对其进行操作,但目前它是我所拥有的最好的。

如果有人有更好的解决方案,我会很高兴听到的。

【讨论】:

【参考方案3】:

我在为我正在从事的项目进行研究时遇到了这个讨论。我必须为移动应用程序创建输入掩码,Pavel Donchev's answer 让我思考如何在 Android 中捕获键。在我的特定项目中,keydown 和 keyup 是不够的,因为 keyup 事件仅在释放密钥后触发,因此这意味着验证较晚,因此我对输入事件进行了更多研究(以及大量试验和错误)并让它工作。

var input = document.getElementById('credit-card-mask'),
    oldValue,
    newValue,
    difference = function(value1, value2) 
      var output = [];
      for(i = 0; i < value2.length; i++) 
        if(value1[i] !== value2[i]) 
          output.push(value2[i]);
        
      
      return output.join("");
    ,
    keyDownHandler = function(e) 
      oldValue = input.value;
      document.getElementById("onkeydown-result").innerhtml = input.value;
    ,
    inputHandler = function(e) 
      newValue = input.value;
      document.getElementById("oninput-result").innerHTML = input.value;
      document.getElementById("typedvalue-result").innerHTML = difference(oldValue, newValue);
    
;

input.addEventListener('keydown', keyDownHandler);
input.addEventListener('input', inputHandler);
<input type="text" id="credit-card-mask" />
<div id="result">
  <h4>on keydown value</h4>
  <div id="onkeydown-result"></div>
  <h4>on input value</h4>
  <div id="oninput-result"></div>
  <h4>typed value</h4>
  <div id="typedvalue-result"></div>
</div>

oninput 事件在 keydown 事件之后立即触发,这是我验证的最佳时机。

我在一篇文章中整理了整个内容。如果你好奇,可以阅读here。

【讨论】:

在堆栈溢出时不鼓励仅链接答案。请在您的回答中添加简要说明。【参考方案4】:

只需检查您输入的字符 keyCode,如果是 0 或 229,那么这里是函数 getKeyCode(),它使用 JS 的 charCodeAt 返回接受输入的 KeyCode字符串一个参数并返回最后一个字符的键码。

<script>
        var getKeyCode = function (str) 
            return str.charCodeAt(str.length);
        


        $('#myTextfield').on('keyup',function(e)

            //for android chrome keycode fix
            if (navigator.userAgent.match(/Android/i)) 

                var inputValue = this.value;

                var charKeyCode = e.keyCode || e.which;
                if (charKeyCode == 0 || charKeyCode == 229)  
                    charKeyCode = getKeyCode(inputValue);
                    alert(charKeyCode+' key Pressed');
                else
                   alert(charKeyCode+' key Pressed');
                    
                   
        );
    </script>

【讨论】:

如果我们在文本框之间的某处输入文本,这将失败 @ImamudinNaseem 是的,在这种情况下,我们将不得不修改与场景匹配的代码。但 AFAIK 这是您可以与虚拟键盘的按键交互以获取键值的唯一方法 AkshayTilekar,酷 :) @ImamudinNaseem :) 这甚至不是一个解决方案。如果我没有在输入框的末尾输入怎么办。【参考方案5】:

有一个textInput 事件为您提供输入的字符

const inputField = document.getElementById('wanted-input-field');

inputField.addEventListener('textInput', function(e) 
    // e.data will be the 1:1 input you done
    const char = e.data; // In our example = "a"

    // If you want the keyCode..
    const keyCode = char.charCodeAt(0); // a = 97

    // Stop processing if "a" is pressed
    if (keyCode === 97) 
        e.preventDefault();
        return false;
    
    return true;
);

【讨论】:

【参考方案6】:

我最近在我们的 Astro AI 辅助电子邮件应用程序的最新版本中实现了“提及”功能。基本上,您在我们的撰写网页视图中键入“@”,您会得到一个自动完成建议的列表。像大多数其他人一样,我们在尝试用 Javascript 解决这个问题时遇到了问题。最终奏效的是本机解决方案。如果你@Override WebView 的 onCreateInputConnection() 方法,你可以用自定义类包装 super.onCreateInputConnection() 结果(它只是一个 InputConnection 接口)。然后在你的包装器实现中,你可以通过 commitText() 或 setComposingText() 或者其他一些特定于你正在寻找的方法来捕获输入......比如删除。我不知道您是否会在控制字符(例如向上和向下箭头)上获得任何回调,但也许这可以作为开始解决您的特定问题的地方。

【讨论】:

【参考方案7】:

我想通了!

这是一个 100% 有效的解决方案,可在任何地方使用所有功能,甚至包括 ios 上的表情符号建议和任何粘贴的内容。我正在使用子字符串比较来查找从 onInput 更改为 onInput 的实际内容。

指出删除文本和插入文本的点。

赞赏并选择作为答案。

var x = document.getElementById("area"),
    text = x.value,
    event_count = 0



function find_Entered_And_Removed_Substrings(
    previous_string, current_string, pos
) 
    let
        right_pos = pos,
        right_boundary_of_removed =
            previous_string.length -
            (
                current_string.length -
                pos
            ),
        left_max_pos = Math.min(
            pos,
            right_boundary_of_removed
        ),
        left_pos = left_max_pos

    for (
        let x = 0; x < left_max_pos; x++
    ) 
        if (
            previous_string[x] !==
            current_string[x]
        ) 
            left_pos = x
            break
        
    


    return 
        left: left_pos,
        right: pos,
        removed_left: left_pos,
        removed_right: right_boundary_of_removed
    


x.oninput =
    (e) => 
        // debugger;
        let
            cur_text = x.value,
            positions =
                find_Entered_And_Removed_Substrings(
                    text, cur_text, Math.max(
                        x.selectionStart, x.selectionEnd
                    )
                )
        console.log(positions)
        let
            entered =
                cur_text.substring(
                    positions.left, positions.right
                ),
            removed =
                text.substring(
                    positions.removed_left, positions.removed_right
                )


        if (
            entered.length >
            0 ||
            removed.length >
            0
        ) 
            document.getElementById("entered")
                .innerHTML +=
                entered

            document.getElementById("removed")
                .innerHTML +=
                removed

            document.getElementById("events")
                .innerHTML =
                event_count++
        


        text = cur_text
    
<textarea id="area"></textarea>
<br/>
<pre id="entered"></pre>
<br/>
<div id="events"></div>
<pre id="removed"></pre>

【讨论】:

感谢用户投票反对此答案的评论。 我猜原因是:格式错误(不常用),根本不可读!调试代码(调试器、console.log) 看起来它不能处理用户在字符串中间输入文本的用例。如果有人试图修正错字,此代码将失败 例如,输入'1245',然后尝试在2之后添加3。 我猜问题是oninput 不够好。您应该改用addEventListener(),并且您可能必须收听所有inputkeyupchangebeforeinput,以确保获得所有修改的事件。某些具有预测 IME 的设备不会触发任何 keyup 事件,inputchange 可能会或可能不会在 IME 输入后立即触发,beforeinput 可能还没有得到足够广泛的支持。根据我的经验,每次更改都会触发其中至少一个事件,因此监听所有这些事件并应用节流可能是最佳选择。

以上是关于使用javascript捕获在android虚拟键盘上键入的键的主要内容,如果未能解决你的问题,请参考以下文章

如何在 JavaScript 中捕获右键单击事件? [复制]

如何通过 JavaScript 捕获 Mac 的命令键?

Xamarin Android 监听音量键(下)

如何在javascript中捕获新标签事件中的打开链接?

android 如何通过app来关机,或者捕获home键

使用 jquery 在 iframe 中捕获鼠标右键单击事件