{"action":"created","issue":{"url":"https://api.github.com/repos/medialize/ally.js/issues/81","labels_url":"https://api.github.com/repos/medialize/ally.js/issues/81/labels{/name}","comments_url":"https://api.github.com/repos/medialize/ally.js/issues/81/comments","events_url":"https://api.github.com/repos/medialize/ally.js/issues/81/events","html_url":"https://github.com/medialize/ally.js/issues/81","id":119495535,"number":81,"title":"add component to safely restore focus","user":{"login":"rodneyrehm","id":186837,"avatar_url":"https://avatars.githubusercontent.com/u/186837?v=3","gravatar_id":"","url":"https://api.github.com/users/rodneyrehm","html_url":"https://github.com/rodneyrehm","followers_url":"https://api.github.com/users/rodneyrehm/followers","following_url":"https://api.github.com/users/rodneyrehm/following{/other_user}","gists_url":"https://api.github.com/users/rodneyrehm/gists{/gist_id}","starred_url":"https://api.github.com/users/rodneyrehm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rodneyrehm/subscriptions","organizations_url":"https://api.github.com/users/rodneyrehm/orgs","repos_url":"https://api.github.com/users/rodneyrehm/repos","events_url":"https://api.github.com/users/rodneyrehm/events{/privacy}","received_events_url":"https://api.github.com/users/rodneyrehm/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/medialize/ally.js/labels/feature","name":"feature","color":"84b6eb"},{"url":"https://api.github.com/repos/medialize/ally.js/labels/focus","name":"focus","color":"207de5"}],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/medialize/ally.js/milestones/3","html_url":"https://github.com/medialize/ally.js/milestones/1.1.0%20-%20second%20wave","labels_url":"https://api.github.com/repos/medialize/ally.js/milestones/3/labels","id":1435649,"number":3,"title":"1.1.0 - second wave","description":"","creator":{"login":"rodneyrehm","id":186837,"avatar_url":"https://avatars.githubusercontent.com/u/186837?v=3","gravatar_id":"","url":"https://api.github.com/users/rodneyrehm","html_url":"https://github.com/rodneyrehm","followers_url":"https://api.github.com/users/rodneyrehm/followers","following_url":"https://api.github.com/users/rodneyrehm/following{/other_user}","gists_url":"https://api.github.com/users/rodneyrehm/gists{/gist_id}","starred_url":"https://api.github.com/users/rodneyrehm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rodneyrehm/subscriptions","organizations_url":"https://api.github.com/users/rodneyrehm/orgs","repos_url":"https://api.github.com/users/rodneyrehm/repos","events_url":"https://api.github.com/users/rodneyrehm/events{/privacy}","received_events_url":"https://api.github.com/users/rodneyrehm/received_events","type":"User","site_admin":false},"open_issues":3,"closed_issues":17,"state":"open","created_at":"2015-11-30T17:14:07Z","updated_at":"2015-12-12T15:08:30Z","due_on":null,"closed_at":null},"comments":0,"created_at":"2015-11-30T13:50:52Z","updated_at":"2015-12-14T16:38:13Z","closed_at":null,"body":"This topic came up while talking jQueryUI integration with @jzaefferer:\r\n\r\n---\r\n\r\nThe dialog tutorial features a [naive method of storing and restoring focus](http://allyjs.io/tutorials/accessible-dialog.html#Restoring-focus-upon-closing-the-dialog). The following scenarios (upon restore) are not unheard of:\r\n\r\n* the element is not focusable anymore\r\n* the element does not exist anymore\r\n\r\n## safe `focus()`\r\n\r\nCalling `element.focus()` on something that is not focusable or attached to the DOM will cause `<body>` to become the `activeElement`. In case we're trying to focus a non-focusable element, we could however traverse the parents to find a focusable element and pass focus to *that* element instead. [`ally.get.focusTarget`](http://allyjs.io/api/get/focus-target.html) should help with that. We would wrap this in `ally.element.focus` and could provide the following options:\r\n\r\n1. \"do not shift focus away from activeElement unless there is a valid target\"\r\n2. \"predefined fallback element to focus if target cannot be resolved\" (also using `ally.get.focusTarget` for convenience)\r\n3. \"try focusing next element in DOM hierarchy\" (before engaging 1 or 2)\r\n4. \"wait until element is focusable\" (using `ally.when.focusable` but not returning the service handle)\r\n\r\n## restore current `activeElement` service\r\n\r\nIf we simply store the `activeElement` and it is detached from the DOM before we restore focus, we can't get its ancestry anymore. For that reason it makes sense to provide a service that upon engaging stores the current element hierarchy. The service handle would expose a function `.restore()` that tries to shift focus to an element in the previously saved hierarchy. At `engage()` and `restore()` a \"fallback\" callback can be passed that is invoked should the service not be able to restore focus.\r\n\r\nShould this be made available at `ally.maintain.restorableFocus`?"},"comment":{"url":"https://api.github.com/repos/medialize/ally.js/issues/comments/164488344","html_url":"https://github.com/medialize/ally.js/issues/81#issuecomment-164488344","issue_url":"https://api.github.com/repos/medialize/ally.js/issues/81","id":164488344,"user":{"login":"jzaefferer","id":52585,"avatar_url":"https://avatars.githubusercontent.com/u/52585?v=3","gravatar_id":"","url":"https://api.github.com/users/jzaefferer","html_url":"https://github.com/jzaefferer","followers_url":"https://api.github.com/users/jzaefferer/followers","following_url":"https://api.github.com/users/jzaefferer/following{/other_user}","gists_url":"https://api.github.com/users/jzaefferer/gists{/gist_id}","starred_url":"https://api.github.com/users/jzaefferer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jzaefferer/subscriptions","organizations_url":"https://api.github.com/users/jzaefferer/orgs","repos_url":"https://api.github.com/users/jzaefferer/repos","events_url":"https://api.github.com/users/jzaefferer/events{/privacy}","received_events_url":"https://api.github.com/users/jzaefferer/received_events","type":"User","site_admin":false},"created_at":"2015-12-14T16:38:13Z","updated_at":"2015-12-14T16:38:13Z","body":"Relevant snippet for restoring activeElement from jQuery UI dialog: https://github.com/jquery/jquery-ui/blob/ffcfb85c9818954adda69e73cf9ba76ea07b554c/ui/widgets/dialog.js#L224-L230 - has some inline comments for WebKit specific issues, might be useful. \r\n\r\nThat uses our [safeBlur](https://github.com/jquery/jquery-ui/blob/ffcfb85c9818954adda69e73cf9ba76ea07b554c/ui/safe-blur.js) and [safeActiveElement](https://github.com/jquery/jquery-ui/blob/ffcfb85c9818954adda69e73cf9ba76ea07b554c/ui/safe-active-element.js) helpers - both have a bunch of support comments as well. \r\n\r\nI haven't looked through ally.js enough to see if any of those aren't yet covered."}}