How to hold key down with Selenium

Andrew K picture Andrew K · Jul 20, 2013 · Viewed 15.7k times · Source

I am using Selenium with Python and Chrome. I am trying to hold down various keys -- specifically "w, a, s, and d." I have found Selenium's action_chains.key_press action as well as the elem.send_keys method. The problem with the first method is that it only holds the key down for as long as it takes to complete an action chain. The problem with the elem.send_keys method is that it does not have an element to send the keys to.

I am trying to control a web-browser based robot with W-A-S-D, so I need to hold keys down for varying durations.

I have tried the following:

action_chains = ActionChains(driver)
action_chains.key_down("w")
action_chains.key_up("w")

as well as:

action_chains.key_press(elem, "w")
for x in range (0, 100):
    action_chains.perform()
    time.sleep(.01)

Neither are ideal.

Answer

Florent B. picture Florent B. · Jun 14, 2017

The current driver for Chrome (version 2.30) implements the previous protocol where holding a key is only supported for a modifier key (Control, Shift, Alt, Command).

So this code works with Firefox but fails with Chrome since the keyUp event is emitted for each keyDown:

action_key_down_w = ActionChains(driver).key_down("w")
action_key_up_w = ActionChains(driver).key_up("w")

endtime = time.time() + 1.0

while True:
  action_key_down_w.perform()

  if time.time() > endtime:
    action_key_up_w.perform()
    break

But, since version 2.30, the Chrome driver supports the send_command to directly send a raw command via the devtools protocol. So as a workaround, you can call Input.dispatchKeyEvent to emit low level events.

This is a working example with Selenium/Chrome to hold the key w during a second:

from selenium import webdriver
import json, time

def dispatchKeyEvent(driver, name, options = {}):
  options["type"] = name
  body = json.dumps({'cmd': 'Input.dispatchKeyEvent', 'params': options})
  resource = "/session/%s/chromium/send_command" % driver.session_id
  url = driver.command_executor._url + resource
  driver.command_executor._request('POST', url, body)

def holdKeyW(driver, duration):
  endtime = time.time() + duration
  options = { \
    "code": "KeyW",
    "key": "w",
    "text": "w",
    "unmodifiedText": "w",
    "nativeVirtualKeyCode": ord("W"),
    "windowsVirtualKeyCode": ord("W")
  }

  while True:
    dispatchKeyEvent(driver, "rawKeyDown", options)
    dispatchKeyEvent(driver, "char", options)

    if time.time() > endtime:
      dispatchKeyEvent(driver, "keyUp", options)
      break

    options["autoRepeat"] = True
    time.sleep(0.01)


driver = webdriver.Chrome()
driver.get("https://stackoverflow.com/questions")

# set the focus on the targeted element
driver.find_element_by_css_selector("input[name=q]").click()

# press the key W during a second
holdKeyW(driver, 1.0)