最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - isssue detecting backspace from mobile keyboards in js - Stack Overflow

programmeradmin1浏览0评论

So i am using a <span> with contenteditable="true". onkeydown generates new elements with the key pressed as text. I use preventDefault() to not show the pressed character in the input field. If "backspace" is pressed, the last new element ist deleted.

It works really well on desktop browsers, but it has an issue on mobile. When testing firefox for android, if I type some letters and then press backspace, none of the events are triggered in js. The virtual keyboard (default and swiftkey tested) somehow keep the last letters and only remove them within the keyboard - only after these are all gone the events get passed to js again.

It is not easy to describe - the scribble shows it. The problem only exists on mobile!!

Resetting the "contenteditable" attribute prevents this behaviour but it also flashes the keyboard, which is not ideal.

//EDIT: fiddle is updated: catching 229, reset caret position, reset input content to " "

let editableSpan = document.querySelector('#inp');

editableSpan.onkeydown = function(e){
    console.log('key pressed', e.which, e.key);
    if(e.key === 'Backspace' || e.key === 'Process'){
    var keyEls = document.querySelectorAll('#out .kbd');
    keyEls.length > 0 && keyEls[keyEls.length - 1].remove();
  }else if(e.key.length === 1){
    var keyEl = document.createElement('span');
    keyEl.classList.add('kbd');
    keyEl.textContent = e.key;
    document.querySelector('#out').append(keyEl);
  }
  e.preventDefault();
}

editableSpan.onkeyup = function(e){
  var letterInp = document.querySelector('#inp');
  var selection = window.getSelection();
  var range     = document.createRange();
  letterInp.innerHTML = '&nbsp;';
  range.setStart(letterInp, letterInp.childNodes.length);
  range.collapse(true);
  selection.removeAllRanges();
  selection.addRange(range);
}
#inp {
  background-color: goldenrod;
  border: solid 3px black;
}
.kbd {
  display:inline-block;
  background-color:darkgrey;
  font-size: 2em;
  padding: .5em;
  margin-right: .3em;
  margin-top: .3em;
  border-radius:.3em;
  font-style:monospace;
}
<span id="out"></span>
<span class="kbd" id="inp" contenteditable="true">&nbsp;</span>

So i am using a <span> with contenteditable="true". onkeydown generates new elements with the key pressed as text. I use preventDefault() to not show the pressed character in the input field. If "backspace" is pressed, the last new element ist deleted.

It works really well on desktop browsers, but it has an issue on mobile. When testing firefox for android, if I type some letters and then press backspace, none of the events are triggered in js. The virtual keyboard (default and swiftkey tested) somehow keep the last letters and only remove them within the keyboard - only after these are all gone the events get passed to js again.

It is not easy to describe - the scribble shows it. The problem only exists on mobile!!

Resetting the "contenteditable" attribute prevents this behaviour but it also flashes the keyboard, which is not ideal.

//EDIT: fiddle is updated: catching 229, reset caret position, reset input content to " "

let editableSpan = document.querySelector('#inp');

editableSpan.onkeydown = function(e){
    console.log('key pressed', e.which, e.key);
    if(e.key === 'Backspace' || e.key === 'Process'){
    var keyEls = document.querySelectorAll('#out .kbd');
    keyEls.length > 0 && keyEls[keyEls.length - 1].remove();
  }else if(e.key.length === 1){
    var keyEl = document.createElement('span');
    keyEl.classList.add('kbd');
    keyEl.textContent = e.key;
    document.querySelector('#out').append(keyEl);
  }
  e.preventDefault();
}

editableSpan.onkeyup = function(e){
  var letterInp = document.querySelector('#inp');
  var selection = window.getSelection();
  var range     = document.createRange();
  letterInp.innerHTML = '&nbsp;';
  range.setStart(letterInp, letterInp.childNodes.length);
  range.collapse(true);
  selection.removeAllRanges();
  selection.addRange(range);
}
#inp {
  background-color: goldenrod;
  border: solid 3px black;
}
.kbd {
  display:inline-block;
  background-color:darkgrey;
  font-size: 2em;
  padding: .5em;
  margin-right: .3em;
  margin-top: .3em;
  border-radius:.3em;
  font-style:monospace;
}
<span id="out"></span>
<span class="kbd" id="inp" contenteditable="true">&nbsp;</span>


Share Improve this question edited Feb 5 at 18:01 Maze asked Feb 4 at 22:34 MazeMaze 1231 silver badge6 bronze badges 7
  • Use e.key and not e.which, as it was deprecated... oh, long time ago. Some browsers may still support it, but you cannot rely on it. Please use e.key and tell us if it solves the problem. The e.key value should be "Backspace". – Sergey A Kryukov Commented Feb 4 at 22:59
  • @Yogi the issue is not related - I am not getting any event trigger on mobile after typing some letters. The answer you suggested gets triggers but expects a different keycode. – Maze Commented Feb 5 at 7:30
  • @SergeyAKryukov I edited the snippet but it is not related to the problem – Maze Commented Feb 5 at 7:30
  • Since resetting the contenteditable attribute works, you should alert what key was pressed on mobile, cause it is not 'Backspace'. – skobaljic Commented Feb 5 at 7:39
  • @skobaljic every input event is logged - and when pressing backspace after typeing some letters in this situation the events are not triggered – Maze Commented Feb 5 at 8:32
 |  Show 2 more comments

1 Answer 1

Reset to default 0

With some pointers from @Yogi I got a working solution.

issue 1: no events fired in js - only happens when caret is placed before "space" -> always set caret position to end of element

issue 2: key=229 is returned in keydown -> handle it the same as backspace and reset the inputs content to " " in onkeyup

发布评论

评论列表(0)

  1. 暂无评论
ok 不同模板 switch ($forum['model']) { /*case '0': include _include(APP_PATH . 'view/htm/read.htm'); break;*/ default: include _include(theme_load('read', $fid)); break; } } break; case '10': // 主题外链 / thread external link http_location(htmlspecialchars_decode(trim($thread['description']))); break; case '11': // 单页 / single page $attachlist = array(); $imagelist = array(); $thread['filelist'] = array(); $threadlist = NULL; $thread['files'] > 0 and list($attachlist, $imagelist, $thread['filelist']) = well_attach_find_by_tid($tid); $data = data_read_cache($tid); empty($data) and message(-1, lang('data_malformation')); $tidlist = $forum['threads'] ? page_find_by_fid($fid, $page, $pagesize) : NULL; if ($tidlist) { $tidarr = arrlist_values($tidlist, 'tid'); $threadlist = well_thread_find($tidarr, $pagesize); // 按之前tidlist排序 $threadlist = array2_sort_key($threadlist, $tidlist, 'tid'); } $allowpost = forum_access_user($fid, $gid, 'allowpost'); $allowupdate = forum_access_mod($fid, $gid, 'allowupdate'); $allowdelete = forum_access_mod($fid, $gid, 'allowdelete'); $access = array('allowpost' => $allowpost, 'allowupdate' => $allowupdate, 'allowdelete' => $allowdelete); $header['title'] = $thread['subject']; $header['mobile_link'] = $thread['url']; $header['keywords'] = $thread['keyword'] ? $thread['keyword'] : $thread['subject']; $header['description'] = $thread['description'] ? $thread['description'] : $thread['brief']; $_SESSION['fid'] = $fid; if ($ajax) { empty($conf['api_on']) and message(0, lang('closed')); $apilist['header'] = $header; $apilist['extra'] = $extra; $apilist['access'] = $access; $apilist['thread'] = well_thread_safe_info($thread); $apilist['thread_data'] = $data; $apilist['forum'] = $forum; $apilist['imagelist'] = $imagelist; $apilist['filelist'] = $thread['filelist']; $apilist['threadlist'] = $threadlist; message(0, $apilist); } else { include _include(theme_load('single_page', $fid)); } break; default: message(-1, lang('data_malformation')); break; } ?>