{"id":1380,"date":"2011-01-23T22:57:37","date_gmt":"2011-01-24T04:57:37","guid":{"rendered":"http:\/\/bililite.nfshost.com\/blog\/?p=1380"},"modified":"2015-01-14T17:42:09","modified_gmt":"2015-01-14T23:42:09","slug":"improved-sendkeys","status":"publish","type":"post","link":"https:\/\/bililite.com\/blog\/2011\/01\/23\/improved-sendkeys\/","title":{"rendered":"Improved <code>sendkeys<\/code>"},"content":{"rendered":"<script src=\"\/inc\/bililiteRange.js\" type=\"text\/javascript\"><\/script>\r\n<script src=\"\/inc\/jquery.sendkeys.js\" type=\"text\/javascript\"><\/script>\r\n<script type=\"text\/javascript\">\r\n$(function(){\r\n\t$('.output, .test, .wrap').css({\r\n\t\tfontSize: '100%',\r\n\t\twidth: '15em',\r\n\t\tpadding: '0.5em',\r\n\t\toverflow: 'auto',\r\n\t\tborder: '1px solid blue'\r\n\t});\r\n\t$('.phonepad ').css({\r\n\t\twidth: '20em',\r\n\t\tborder: '1px solid gray',\r\n\t\tpadding: '0px',\r\n\t\tfloat: 'left'\r\n\t});\r\n\t$('.phonepad input').css({\r\n\t\twidth: '3em',\r\n\t\theight: '2em',\r\n\t\tmargin: '.5em'\r\n\t});\r\n\t$('div.test').css({\r\n\t\tborder: '1px solid gray'\r\n\t});\r\n});\r\n<\/script>\r\n<h4 style=\"clear: both;\">The <code>$.fn.sendkeys<\/code> Plugin<\/h4>\r\n<p><strong>This is now obsolete. <code>sendkeys<\/code> is at version 4, and is documented at \"<a href=\"http:\/\/bililite.com\/blog\/2015\/01\/14\/rethinking-fn-sendkeys\/\" title=\"Rethinking $.fn.sendkeys\">Rethinking <code>$.fn.sendkeys<\/code><\/a>\".<\/strong><\/p>\r\n<p>I wanted to make a general-purpose onscreen keypad, and wasted a huge amount of time trying to find a way to\r\nsimulate a keypress. \r\n<code class=\"language-javascript\">$(element).trigger(\"keypress\",...)<\/code> won't work. Neither will keyup or keydown.\r\nFor security reasons, I guess, you can't tell an element to pretend a key was pressed. The browser is too\r\nworried that you will access menus or something.<\/p>\r\n<p>So I wrote my own plugin and named it after Microsoft's <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/8c6yea83%28VS.85%29.aspx\">sendkeys<\/a> \r\nwhich does similar things. For any editable element <code>elem<\/code>,\r\n<code class=\"language-javascript\">$(elem).sendkeys(string)<\/code> inserts <code>string<\/code> at the insertion point or selection. \r\nIt's the insertion point sensitivity\r\nthat makes it more sophisticated than <code>elem.value += string<\/code>.<\/p>\r\n<p>It depends on my <a href=\"\/blog\/2011\/01\/17\/cross-browser-text-ranges-and-selections\/\">bililiteRange routines<\/a> to manage the selections in a cross-browser way.\r\n<h4>Downloads<\/h4>\r\n<p>See <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/jquery.sendkeys.js\">$.fn.sendkeys<\/a> on github.<\/p>\r\n<p>See <a href=\"https:\/\/github.com\/dwachss\/bililiteRange\/blob\/master\/bililiteRange.js\">bililiteRange<\/a> on github.<\/p>\r\n<p>See the <a href=\"\/blog\/2011\/01\/23\/improved-sendkeys\/#demo\">demo<\/a>.<\/p>\r\n<!--more-->\r\n<h4>Usage<\/h4>\r\n<p>It's very simple, <code class=\"language-javascript\">$(element).sendkeys(string)<\/code> inserts <code>string<\/code> at the insertion point in an <code>input<\/code>, <code>textarea<\/code> or other element with <code>contenteditable=true<\/code>. If the insertion point is not currently in the element, it remembers where the insertion point was when <code>sendkeys<\/code> was last called (if the insertion point was never in the element, it appends to the end).<\/p>\r\n<h4>Special keys<\/h4>\r\n<p>\"Special keys\" are indicated by curly braces. The following are defined:\r\n<dl>\r\n<dt><code>{backspace}<\/code><\/dt><dd>Delete backwards<\/dd>\r\n<dt><code>{del}<\/code><\/dt><dd>Delete forwards<\/dd> \r\n<dt><code>{rightarrow}<\/code><\/dt><dd>Move the insertion point to the right<\/dd>\r\n<dt><code>{leftarrow}<\/code><\/dt><dd>Move the insertion point to the left<\/dd>\r\n<dt><code>{selectall}<\/code><\/dt><dd>Select the entire field<\/dd>\r\n<dt><code>{enter}<\/code><\/dt><dd>Insert a newline. <strong>Warning:<\/strong> In <code>contenteditable<\/code> elements, <code>{enter}<\/code> is flaky and inconsistent across browsers. This is due to the flakiness of <code>contenteditable<\/code> itself; I can't figure out what to do about this.<\/dd>\r\n<dt><code>{tab}<\/code><\/dt><dd>Insert a <code class=\"language-javascript\" >'\\t'<\/code> character. <code class=\"language-javascript\" >$().sendkeys('\\t')<\/code> would work just as well, but there are circumstances when I wanted to avoid having to escape backslashes.<\/dd>\r\n<dt><code>{newline}<\/code><\/dt><dd>Insert a <code class=\"language-javascript\" >'\\n'<\/code> character, without the mangling that <code>{enter}<\/code> does.<\/dd>\r\n<dt><code>{{}<\/code><\/dt><dd>Inserts a <code>{<\/code> by itself<\/dd>\r\n<dt><code>{selection}<\/code><\/dt><dd>Inserts the text of the original selection (useful for creating \"wrapping\" functions, like <code>\"&lt;em&gt;{selection}&lt;\/em&gt;\"<\/code>).<\/dd>\r\n<dt><code>{mark}<\/code><\/dt><dd>Remembers the current insertion point and restores it after the <code>sendkeys<\/code> call. Thus <code>\"&lt;p&gt;{mark}&lt;\/p&gt;\"<\/code> inserts <code>&lt;p&gt;&lt;\/p&gt;<\/code> and leaves the insertion point between the tags.<\/dd>\r\n<\/dl>\r\nSo \r\n<code class=\"language-javascript\">$(elem).sendkeys('1234')<\/code> inserts <code>1234<\/code>,\r\n<code class=\"language-javascript\">$(elem).sendkeys('123{backspace}4')<\/code> inserts <code>124<\/code>, and\r\n<code class=\"language-javascript\">$(elem).sendkeys('1234{leftarrow}{leftarrow}{leftarrow}{del}')<\/code> inserts <code>134<\/code>, and\r\n<code class=\"language-javascript\">$(elem).sendkeys('&lt;a href=\"{mark}\"&gt;{selection}&lt;\/a&gt;')<\/code> turns the current selection into the text of a hyperlink and leaves the insertion point in position to type the link itself.\r\n<\/p>\r\n<p>I used Microsoft's key-escaping notation rather than backslashes because putting backslashes in strings means escaping <em>them<\/em>, and I\r\nalways get lost in the forest of slashes. Unlike the Microsoft function, this does not use metacharacters (+^%~).<\/p>\r\n<h4>Special keys<\/h4>\r\n<p>There are no real options to the plugin call itself, but you can set your own special keys in the <a href=\"\/blog\/2011\/01\/17\/cross-browser-text-ranges-and-selections\/\"><code>bililiteRange.sendkeys<\/code><\/a>\r\nobject. The defaults are:<\/p>\r\n<pre><code class=\"language-javascript\">\r\nbililiteRange.sendkeys = {\r\n\t'{backspace}': function (rng){...},\r\n\t'{rightarrow}': function (rng){...},\r\n\t'{leftarrow}': function (rng){...},\r\n\tetc.\r\n};\r\n<\/code><\/pre>\r\n<p><code class=\"language-javascript\" >rng<\/code> is the bililiteRange object being manipulated. The default action (for anything not in braces) is <code class=\"language-javascript\" >rng.text(string, 'end')<\/code>. You can create synonyms easily, like <code class=\"language-javascript\">bililiteRange.sendkeys['{BS}'] = bililiteRange.sendkeys['{backspace}']<\/code>. And adding new functions is like <code class=\"language-javascript\" >bililiteRange.sendkeys['{selectall}'] = function(rng) {rng.bounds('all')}<\/code>. Using an undefined \"specialkey\" just inserts the name, so <code class=\"language-javascript\" >$(el).sendkeys('{foo}')<\/code> inserts <code class=\"language-javascript\" >'foo'<\/code>. The original text of the range is stored in <code class=\"language-javascript\" >rng.data().sendkeysOriginalText<\/code>, so a way to show the selected text would be <code class=\"language-javascript\" >bililiteRange.sendkeys['{console}'] = function (rng) {console.log(rng.data().sendkeysOriginalText)<\/code>.<\/p>\r\n\r\n<p>The jQuery plugin (but not the bililiteRange function) turns <code class=\"language-javascript\" >'\\n'<\/code> into <code class=\"language-javascript\" >'{enter}'<\/code> in the input string. It can be escaped to just insert the newline by quoting it in braces: <code class=\"language-javascript\" >'{\\n}'<\/code>. This mangling turned out to be less useful than I thought, but I kept it in for backwards compatibility.<\/p>\r\n\r\n<h4 id=bililiteRangesendkeys><code>bililiteRange<\/code> function<\/h4>\r\n<p>The jQuery plugin is just a wrapper for the <code class=\"language-javascript\" >bililiteRange(element).sendkeys(string)<\/code> function. The plugin is simply:<\/p>\r\n<pre><code class=\"language-javascript\" >$.fn.sendkeys = function (x){\r\n\tx = x.replace(\/([^{])\\n\/g, '$1{enter}'); \/\/ turn line feeds into explicit break insertions, but not if escaped\r\n\treturn this.each( function(){\r\n\t\tbililiteRange(this).bounds('selection').sendkeys(x).select();\r\n\t\tthis.focus();\r\n\t});\r\n}; \/\/ sendkeys<\/code><\/pre>\r\n<p>So the plugin manipulates the element (acting on the selection, then selecting the result), while the bililiteRange function affects the text but does not change the selection.<\/p>\r\n\r\n<h4>Events<\/h4>\r\n<p>To help simulate an actual keypress, the plugin does a <code class=\"language-javascript\">trigger('keypress')<\/code> with the <code>event<\/code>\r\n<code>keyCode<\/code>, <code>charCode<\/code> and <code>which<\/code> all set to the ascii value of the letter sent. This is only triggered with <code>simplechar<\/code>; special characters do not trigger that event.<\/p>\r\n<p>In addition, <code>trigger('sendkeys')<\/code> (a custom event) is executed, with <code>event.which<\/code> set to the original string that was sent.<\/p>\r\n\r\n<pre><code class=\"language-javascript demo\">\r\n$('.selectoutput').click(function(){\r\n\t$('.output').removeClass('selected');\r\n\tvar index = $(this).parents('th').index();\r\n\t$('.output').eq(index).addClass('selected').focus();\r\n});\r\n$('div.test input:button').click(function(){\r\n\t$('.output.selected').sendkeys($('div.test input:text').val());\r\n});\r\n$('div.wrap input:button').click(function(){\r\n\tvar tag = $('div.wrap select').val();\r\n\t$('.output.selected').sendkeys('&lt;'+tag+'&gt;{selection}{mark}&lt;\/'+tag+'&gt;');\r\n});\r\n$('.phonepad input').click(function(){\r\n\t$('.output.selected').sendkeys(this.name || this.value);\r\n});\r\n$('.output').each(function(){\r\n\tbililiteRange(this); \/\/ initialize the selection tracking\r\n}).on('keypress', function(evt){\r\n\t$('#keypress').text($('#keypress').text()+' '+evt.which);\r\n}).on('sendkeys', function(evt){\r\n\t$('#sendkeys').text($('#sendkeys').text()+' '+evt.which);\r\n}).on('focus', function(){\r\n\tvar index = $(this).parents('td').index();\r\n\t$('.output').removeClass('selected');\r\n\t$('.output').eq(index).addClass('selected')\r\n\t$('.selectoutput').eq(index).attr('checked',true);;\r\n});\r\n<\/code><\/pre>\r\n<pre><code class=\"language-html demo\">\r\n&lt;div&gt;\r\n\t&lt;table style=&quot;width: 100%&quot; border=&quot;2&quot; id=\"demo\" &gt;\r\n\t\t&lt;thead&gt;\r\n\t\t\t&lt;tr&gt;\r\n\t\t\t\t&lt;th&gt;&lt;label&gt;\r\n\t\t\t\t\t&lt;input type=&quot;radio&quot; class=\"selectoutput\" name=&quot;selectoutput&quot; checked=\"checked\" \/&gt;\r\n\t\t\t\t\t&lt;code&gt;&amp;lt;input&amp;gt;&lt;\/code&gt;\r\n\t\t\t\t&lt;\/label&gt;&lt;\/th&gt;\r\n\t\t\t\t&lt;th&gt;&lt;label&gt;\r\n\t\t\t\t\t&lt;input type=&quot;radio&quot; class=\"selectoutput\" name=&quot;selectoutput&quot; \/&gt;\r\n\t\t\t\t\t&lt;code&gt;&amp;lt;textarea&amp;gt;&lt;\/code&gt;\r\n\t\t\t\t&lt;\/label&gt;&lt;\/th&gt;\r\n\t\t\t\t&lt;th&gt;&lt;label&gt;\r\n\t\t\t\t\t&lt;input type=&quot;radio&quot; class=\"selectoutput\" name=&quot;selectoutput&quot; \/&gt;\r\n\t\t\t\t\t&lt;code&gt;&amp;lt;div&amp;gt;&lt;\/code&gt;\r\n\t\t\t\t&lt;\/label&gt;&lt;\/th&gt;\r\n\t\t\t&lt;\/tr&gt;\r\n\t\t&lt;\/thead&gt;\r\n\t\t&lt;tbody&gt;\r\n\t\t\t&lt;tr&gt;\r\n\t\t\t\t&lt;td&gt;&lt;input type=&quot;text&quot; class=&quot;output selected&quot; \/&gt;&lt;\/td&gt;\r\n\t\t\t\t&lt;td&gt;&lt;textarea class=&quot;output&quot;&gt;&lt;\/textarea&gt;&lt;\/td&gt;\r\n\t\t\t\t&lt;td&gt;&lt;div class=&quot;output&quot; contentEditable=\"true\"&gt;&lt;\/div&gt;&lt;\/td&gt;\r\n\t\t\t&lt;\/tr&gt;\r\n\t\t&lt;\/tbody&gt;\r\n\t&lt;\/table&gt;\r\n&lt;div class=&quot;phonepad&quot;&gt;\r\n&lt;input type=&quot;button&quot; name=&quot;{leftarrow}&quot; value=&quot;&amp;larr;&quot;\/&gt;&lt;input type=&quot;button&quot; name=&quot;{rightarrow}&quot; value=&quot;&amp;rarr;&quot;\/&gt;&lt;input type=&quot;button&quot; name=&quot;{backspace}&quot; value=&quot;BS&quot;\/&gt;&lt;input type=&quot;button&quot; name=&quot;{selectall}&quot; value=&quot;All&quot;\/&gt;&lt;br\/&gt;\r\n&lt;input type=&quot;button&quot; value=&quot;7&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;8&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;9&quot; \/&gt;&lt;br\/&gt;\r\n&lt;input type=&quot;button&quot; value=&quot;4&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;5&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;6&quot; \/&gt;&lt;br\/&gt;\r\n&lt;input type=&quot;button&quot; value=&quot;1&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;2&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;3&quot; \/&gt;&lt;br\/&gt;\r\n&lt;input type=&quot;button&quot; value=&quot;*&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;0&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;#&quot; \/&gt;&lt;input type=&quot;button&quot; name=&quot;{enter}&quot; value=&quot;&amp;crarr;&quot;\/&gt;&lt;\/div&gt;\r\n&lt;div class=&quot;test&quot;&gt;&lt;input type=&quot;text&quot; \/&gt;&lt;input type=&quot;button&quot; value=&quot;test&quot;\/&gt;&lt;\/div&gt;\r\n&lt;div class=&quot;wrap&quot;&gt;&lt;select&gt;&lt;option&gt;em&lt;\/option&gt;&lt;option&gt;strong&lt;\/option&gt;&lt;option&gt;del&lt;\/option&gt;&lt;\/select&gt;&lt;input type=&quot;button&quot; value=&quot;Wrap Selection&quot;\/&gt;&lt;\/div&gt;\r\n&lt;p&gt;The test button will send the entered string directly. The wrap button will wrap the current selection with the given tag and leave the insertion point after the selection (uses &lt;code&gt;&amp;lt;tag&amp;gt;{selection}{mark}&amp;lt;\/tag&amp;gt;&lt;\/code&gt;).&lt;\/p&gt;\r\n&lt;div id=\"keypress\"&gt;keypress event.which:&lt;\/div&gt;\r\n&lt;div id=\"sendkeys\"&gt;sendkeys event.which:&lt;\/div&gt;\r\n&lt;\/div&gt;\r\n<\/code><span id=\"demo\"><\/span><\/pre>","protected":false},"excerpt":{"rendered":"The $.fn.sendkeys Plugin This is now obsolete. sendkeys is at version 4, and is documented at \"Rethinking $.fn.sendkeys\". I wanted to make a general-purpose onscreen keypad, and wasted a huge amount of time trying to find a way to simulate a keypress. $(element).trigger(\"keypress\",...) won't work. Neither will keyup or keydown. For security reasons, I guess, [&hellip;]","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10,5],"tags":[],"_links":{"self":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1380"}],"collection":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/comments?post=1380"}],"version-history":[{"count":40,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1380\/revisions"}],"predecessor-version":[{"id":3462,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/posts\/1380\/revisions\/3462"}],"wp:attachment":[{"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/media?parent=1380"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/categories?post=1380"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bililite.com\/blog\/wp-json\/wp\/v2\/tags?post=1380"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}