{"action":"created","comment":{"url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/comments/138543885","pull_request_review_id":62350940,"id":138543885,"diff_hunk":"@@ -0,0 +1,181 @@\n+---\n+title: A clickoutside directive\n+type: cookbook\n+order: 2.1\n+---\n+\n+## What we are building\n+\n+Many UI elements need to react to clicks that happen *outside* of them. Common use cases are:\n+\n+* Modals\n+* Dropdown menus\n+* Popovers\n+* Image Lightboxes\n+\n+We will build a small [Custom Directive](https://vuejs.org/v2/guide/custom-directive.html) that we can use in any component in need of this behaviour. One such component is the [modal example](https://vuejs.org/v2/examples/modal.html) from the Vue.js website, so we will use this as a base component to demonstrate the usage of our new directive.\n+\n+> If you haven't already read the our Vue.js Guide about custom directives, we recommend you change this now. [Click here](../guide/custom-directive.html) to got to the relevant section in the guide.\n+\n+Ready? Great. So this is what the endresult of our efforts will look like:\n+\n+```html\n+<modal v-clickoutside=\"handler\">\n+  <!--  \n+    \"handler\" should be a method in your component.\n+    It will be called when the user clicks outside of the modal window\n+   -->\n+</modal>\n+```\n+\n+## bind(): Adding the desired behavior\n+\n+The basic functionality is easy enough to achive. We register a new directive with Vue, and use the `bind()` hook to register an event listener on the document:\n+\n+```javascript\n+Vue.directive('clickoutside', {\n+  bind(el, binding) {\n+    // get the \"handler\" function that gets passed to the directive.\n+    const handler = binding.value\n+    // some error checking\n+    if (typof handler !== 'functionality') {\n+      console.warn('[vue-clickoutside]: bind value must be a function.')\n+      return\n+    }\n+\n+    setTimeout(function() {\n+      document.addEventListener('click', function(event) {\n+        const target = event.target\n+        // check weither the click happened outside of el\n+        if (!el.contains(target) && el !== target) {\n+          handler(event)\n+        }\n+      })\n+    }, 0)\n+  }\n+})\n+```\n+\n+So what happened here?\n+\n+* When the directive is bound to the element, its `bind()` hook is called.\n+* In this hook, we add an Event listener to the document, which will call the handler of the component for *any* click happening on our page.\n+* We use `setTimeout()` because if we didn't, then a `click` event from a button that is responsible to make our modal appear could trigger our directive's handler immediatly, and we don't want that, of course.\n+* And to make sure that this handler is only called when the click actually happened outside of out element `el`, we first check that the event's target wasn't `el` or one of its child nodes.\n+\n+## unbind(): Cleaning up after ourselves\n+\n+Our directive does the job, but it still has a flaw: We have no mechanism in place to remove the listener again if `el` is removed from the DOM. This is problematic because the usual usecase of our directive on a `<modal>`will be to close a modal, for example, so our directive will be gone after this too - and so should the listener.\n+\n+We can correct that with the `unbind()` hook, but there's a catch we have to work around: since directives don't have instances, we can't save the handler method on it. To solve this challenge, we have two possibilities: we can either cache the hander on the element, or we can use a ES6 [Map](https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Map). Using the latter is much cleaner because we don't have to touch the element, but requires a polyfill for older browsers. We will show you both ways here.\n+\n+#### Saving the handler on the element\n+\n+```JavaScript\n+Vue.directive('clickoutside', {\n+  bind(el, binding) {\n+    const handler = binding.value\n+\n+    // create a named function for the handler\n+    function wrappedHandler(event) {\n+      const target = event.target\n+      if (!el.contains(target) && el !== target) {\n+        handler(event)\n+      }\n+    }\n+\n+    // and save it in a property on the element\n+    el.__vueClickOutside__ = wrappedHandler\n+\n+    setTimeout(function() {\n+      document.addEventListener('click', wrappedHandler)\n+    }, 0)\n+  },\n+\n+  unbind(el) {\n+\n+    if (el.__vueClickOutside__) {\n+      // retrieve the handler from the element\n+      const handler = el.__vueClickOutside__\n+      // and remove it from the document's click listeners\n+      document.removeEventListener('click', handler)\n+    }\n+\n+  }\n+})\n+```\n+\n+#### Using a ES6 Map to cache the handler\n+\n+```JavaScript\n+\n+// we create the map outside of the directive,\n+// because we only need one.\n+// We don't have to create one for each instance of our directive.\n+var handlerCache = new Map()\n+\n+Vue.directive('clickoutside', {\n+  bind(el, binding) {\n+    // ...\n+    // el.__vueClickOutside__ = wrappedHandler\n+    handlerCache.set(el, wrappedHandler)\n+    // ...\n+  },\n+  unbind(el) {\n+\n+    if (handlerCache.has(el)) {\n+      // get the handler from the Map\n+      const hander = handlerCache.get(el)","path":"src/v2/cookbook/clickoutside-directive.md","position":128,"original_position":128,"commit_id":"7aed35bf634483b0c1fae0d6c6defdbd845cecec","original_commit_id":"7aed35bf634483b0c1fae0d6c6defdbd845cecec","user":{"login":"LucasLeandro1204","id":13268691,"avatar_url":"https://avatars2.githubusercontent.com/u/13268691?v=4","gravatar_id":"","url":"https://api.github.com/users/LucasLeandro1204","html_url":"https://github.com/LucasLeandro1204","followers_url":"https://api.github.com/users/LucasLeandro1204/followers","following_url":"https://api.github.com/users/LucasLeandro1204/following{/other_user}","gists_url":"https://api.github.com/users/LucasLeandro1204/gists{/gist_id}","starred_url":"https://api.github.com/users/LucasLeandro1204/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LucasLeandro1204/subscriptions","organizations_url":"https://api.github.com/users/LucasLeandro1204/orgs","repos_url":"https://api.github.com/users/LucasLeandro1204/repos","events_url":"https://api.github.com/users/LucasLeandro1204/events{/privacy}","received_events_url":"https://api.github.com/users/LucasLeandro1204/received_events","type":"User","site_admin":false},"body":"handler","created_at":"2017-09-13T07:34:36Z","updated_at":"2017-09-13T07:34:37Z","html_url":"https://github.com/vuejs/vuejs.org/pull/681#discussion_r138543885","pull_request_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681","author_association":"NONE","_links":{"self":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/comments/138543885"},"html":{"href":"https://github.com/vuejs/vuejs.org/pull/681#discussion_r138543885"},"pull_request":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681"}}},"pull_request":{"url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681","id":99802652,"html_url":"https://github.com/vuejs/vuejs.org/pull/681","diff_url":"https://github.com/vuejs/vuejs.org/pull/681.diff","patch_url":"https://github.com/vuejs/vuejs.org/pull/681.patch","issue_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/681","number":681,"state":"open","locked":false,"title":"Cookbook v clickoutside","user":{"login":"LinusBorg","id":1444526,"avatar_url":"https://avatars3.githubusercontent.com/u/1444526?v=4","gravatar_id":"","url":"https://api.github.com/users/LinusBorg","html_url":"https://github.com/LinusBorg","followers_url":"https://api.github.com/users/LinusBorg/followers","following_url":"https://api.github.com/users/LinusBorg/following{/other_user}","gists_url":"https://api.github.com/users/LinusBorg/gists{/gist_id}","starred_url":"https://api.github.com/users/LinusBorg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LinusBorg/subscriptions","organizations_url":"https://api.github.com/users/LinusBorg/orgs","repos_url":"https://api.github.com/users/LinusBorg/repos","events_url":"https://api.github.com/users/LinusBorg/events{/privacy}","received_events_url":"https://api.github.com/users/LinusBorg/received_events","type":"User","site_admin":false},"body":"Add Cookbook chapter about a clickoutside directive.\r\n\r\nAnybody care to review? I'm good at having typos *everyhere* :D","created_at":"2016-12-31T10:52:03Z","updated_at":"2017-09-13T07:34:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"703bb28ddc6a3edef0671d469dbc36c4a5c82dd3","assignee":{"login":"LinusBorg","id":1444526,"avatar_url":"https://avatars3.githubusercontent.com/u/1444526?v=4","gravatar_id":"","url":"https://api.github.com/users/LinusBorg","html_url":"https://github.com/LinusBorg","followers_url":"https://api.github.com/users/LinusBorg/followers","following_url":"https://api.github.com/users/LinusBorg/following{/other_user}","gists_url":"https://api.github.com/users/LinusBorg/gists{/gist_id}","starred_url":"https://api.github.com/users/LinusBorg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LinusBorg/subscriptions","organizations_url":"https://api.github.com/users/LinusBorg/orgs","repos_url":"https://api.github.com/users/LinusBorg/repos","events_url":"https://api.github.com/users/LinusBorg/events{/privacy}","received_events_url":"https://api.github.com/users/LinusBorg/received_events","type":"User","site_admin":false},"assignees":[{"login":"LinusBorg","id":1444526,"avatar_url":"https://avatars3.githubusercontent.com/u/1444526?v=4","gravatar_id":"","url":"https://api.github.com/users/LinusBorg","html_url":"https://github.com/LinusBorg","followers_url":"https://api.github.com/users/LinusBorg/followers","following_url":"https://api.github.com/users/LinusBorg/following{/other_user}","gists_url":"https://api.github.com/users/LinusBorg/gists{/gist_id}","starred_url":"https://api.github.com/users/LinusBorg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LinusBorg/subscriptions","organizations_url":"https://api.github.com/users/LinusBorg/orgs","repos_url":"https://api.github.com/users/LinusBorg/repos","events_url":"https://api.github.com/users/LinusBorg/events{/privacy}","received_events_url":"https://api.github.com/users/LinusBorg/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681/commits","review_comments_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681/comments","review_comment_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls/comments{/number}","comments_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/681/comments","statuses_url":"https://api.github.com/repos/vuejs/vuejs.org/statuses/7aed35bf634483b0c1fae0d6c6defdbd845cecec","head":{"label":"vuejs:cookbook_v-clickoutside","ref":"cookbook_v-clickoutside","sha":"7aed35bf634483b0c1fae0d6c6defdbd845cecec","user":{"login":"vuejs","id":6128107,"avatar_url":"https://avatars1.githubusercontent.com/u/6128107?v=4","gravatar_id":"","url":"https://api.github.com/users/vuejs","html_url":"https://github.com/vuejs","followers_url":"https://api.github.com/users/vuejs/followers","following_url":"https://api.github.com/users/vuejs/following{/other_user}","gists_url":"https://api.github.com/users/vuejs/gists{/gist_id}","starred_url":"https://api.github.com/users/vuejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vuejs/subscriptions","organizations_url":"https://api.github.com/users/vuejs/orgs","repos_url":"https://api.github.com/users/vuejs/repos","events_url":"https://api.github.com/users/vuejs/events{/privacy}","received_events_url":"https://api.github.com/users/vuejs/received_events","type":"Organization","site_admin":false},"repo":{"id":15644594,"name":"vuejs.org","full_name":"vuejs/vuejs.org","owner":{"login":"vuejs","id":6128107,"avatar_url":"https://avatars1.githubusercontent.com/u/6128107?v=4","gravatar_id":"","url":"https://api.github.com/users/vuejs","html_url":"https://github.com/vuejs","followers_url":"https://api.github.com/users/vuejs/followers","following_url":"https://api.github.com/users/vuejs/following{/other_user}","gists_url":"https://api.github.com/users/vuejs/gists{/gist_id}","starred_url":"https://api.github.com/users/vuejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vuejs/subscriptions","organizations_url":"https://api.github.com/users/vuejs/orgs","repos_url":"https://api.github.com/users/vuejs/repos","events_url":"https://api.github.com/users/vuejs/events{/privacy}","received_events_url":"https://api.github.com/users/vuejs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/vuejs/vuejs.org","description":"The website.","fork":false,"url":"https://api.github.com/repos/vuejs/vuejs.org","forks_url":"https://api.github.com/repos/vuejs/vuejs.org/forks","keys_url":"https://api.github.com/repos/vuejs/vuejs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vuejs/vuejs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vuejs/vuejs.org/teams","hooks_url":"https://api.github.com/repos/vuejs/vuejs.org/hooks","issue_events_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/events{/number}","events_url":"https://api.github.com/repos/vuejs/vuejs.org/events","assignees_url":"https://api.github.com/repos/vuejs/vuejs.org/assignees{/user}","branches_url":"https://api.github.com/repos/vuejs/vuejs.org/branches{/branch}","tags_url":"https://api.github.com/repos/vuejs/vuejs.org/tags","blobs_url":"https://api.github.com/repos/vuejs/vuejs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vuejs/vuejs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vuejs/vuejs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/vuejs/vuejs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vuejs/vuejs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/vuejs/vuejs.org/languages","stargazers_url":"https://api.github.com/repos/vuejs/vuejs.org/stargazers","contributors_url":"https://api.github.com/repos/vuejs/vuejs.org/contributors","subscribers_url":"https://api.github.com/repos/vuejs/vuejs.org/subscribers","subscription_url":"https://api.github.com/repos/vuejs/vuejs.org/subscription","commits_url":"https://api.github.com/repos/vuejs/vuejs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/vuejs/vuejs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/vuejs/vuejs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/vuejs/vuejs.org/contents/{+path}","compare_url":"https://api.github.com/repos/vuejs/vuejs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vuejs/vuejs.org/merges","archive_url":"https://api.github.com/repos/vuejs/vuejs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vuejs/vuejs.org/downloads","issues_url":"https://api.github.com/repos/vuejs/vuejs.org/issues{/number}","pulls_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/vuejs/vuejs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/vuejs/vuejs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vuejs/vuejs.org/labels{/name}","releases_url":"https://api.github.com/repos/vuejs/vuejs.org/releases{/id}","deployments_url":"https://api.github.com/repos/vuejs/vuejs.org/deployments","created_at":"2014-01-05T03:11:14Z","updated_at":"2017-09-13T02:48:44Z","pushed_at":"2017-09-13T05:04:34Z","git_url":"git://github.com/vuejs/vuejs.org.git","ssh_url":"git@github.com:vuejs/vuejs.org.git","clone_url":"https://github.com/vuejs/vuejs.org.git","svn_url":"https://github.com/vuejs/vuejs.org","homepage":"vuejs.org","size":36102,"stargazers_count":1182,"watchers_count":1182,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":1327,"mirror_url":null,"open_issues_count":62,"forks":1327,"open_issues":62,"watchers":1182,"default_branch":"master"}},"base":{"label":"vuejs:master","ref":"master","sha":"330267d477a56e640b5cb2e2e9d04aa810442445","user":{"login":"vuejs","id":6128107,"avatar_url":"https://avatars1.githubusercontent.com/u/6128107?v=4","gravatar_id":"","url":"https://api.github.com/users/vuejs","html_url":"https://github.com/vuejs","followers_url":"https://api.github.com/users/vuejs/followers","following_url":"https://api.github.com/users/vuejs/following{/other_user}","gists_url":"https://api.github.com/users/vuejs/gists{/gist_id}","starred_url":"https://api.github.com/users/vuejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vuejs/subscriptions","organizations_url":"https://api.github.com/users/vuejs/orgs","repos_url":"https://api.github.com/users/vuejs/repos","events_url":"https://api.github.com/users/vuejs/events{/privacy}","received_events_url":"https://api.github.com/users/vuejs/received_events","type":"Organization","site_admin":false},"repo":{"id":15644594,"name":"vuejs.org","full_name":"vuejs/vuejs.org","owner":{"login":"vuejs","id":6128107,"avatar_url":"https://avatars1.githubusercontent.com/u/6128107?v=4","gravatar_id":"","url":"https://api.github.com/users/vuejs","html_url":"https://github.com/vuejs","followers_url":"https://api.github.com/users/vuejs/followers","following_url":"https://api.github.com/users/vuejs/following{/other_user}","gists_url":"https://api.github.com/users/vuejs/gists{/gist_id}","starred_url":"https://api.github.com/users/vuejs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vuejs/subscriptions","organizations_url":"https://api.github.com/users/vuejs/orgs","repos_url":"https://api.github.com/users/vuejs/repos","events_url":"https://api.github.com/users/vuejs/events{/privacy}","received_events_url":"https://api.github.com/users/vuejs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/vuejs/vuejs.org","description":"The website.","fork":false,"url":"https://api.github.com/repos/vuejs/vuejs.org","forks_url":"https://api.github.com/repos/vuejs/vuejs.org/forks","keys_url":"https://api.github.com/repos/vuejs/vuejs.org/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vuejs/vuejs.org/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vuejs/vuejs.org/teams","hooks_url":"https://api.github.com/repos/vuejs/vuejs.org/hooks","issue_events_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/events{/number}","events_url":"https://api.github.com/repos/vuejs/vuejs.org/events","assignees_url":"https://api.github.com/repos/vuejs/vuejs.org/assignees{/user}","branches_url":"https://api.github.com/repos/vuejs/vuejs.org/branches{/branch}","tags_url":"https://api.github.com/repos/vuejs/vuejs.org/tags","blobs_url":"https://api.github.com/repos/vuejs/vuejs.org/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vuejs/vuejs.org/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vuejs/vuejs.org/git/refs{/sha}","trees_url":"https://api.github.com/repos/vuejs/vuejs.org/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vuejs/vuejs.org/statuses/{sha}","languages_url":"https://api.github.com/repos/vuejs/vuejs.org/languages","stargazers_url":"https://api.github.com/repos/vuejs/vuejs.org/stargazers","contributors_url":"https://api.github.com/repos/vuejs/vuejs.org/contributors","subscribers_url":"https://api.github.com/repos/vuejs/vuejs.org/subscribers","subscription_url":"https://api.github.com/repos/vuejs/vuejs.org/subscription","commits_url":"https://api.github.com/repos/vuejs/vuejs.org/commits{/sha}","git_commits_url":"https://api.github.com/repos/vuejs/vuejs.org/git/commits{/sha}","comments_url":"https://api.github.com/repos/vuejs/vuejs.org/comments{/number}","issue_comment_url":"https://api.github.com/repos/vuejs/vuejs.org/issues/comments{/number}","contents_url":"https://api.github.com/repos/vuejs/vuejs.org/contents/{+path}","compare_url":"https://api.github.com/repos/vuejs/vuejs.org/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vuejs/vuejs.org/merges","archive_url":"https://api.github.com/repos/vuejs/vuejs.org/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vuejs/vuejs.org/downloads","issues_url":"https://api.github.com/repos/vuejs/vuejs.org/issues{/number}","pulls_url":"https://api.github.com/repos/vuejs/vuejs.org/pulls{/number}","milestones_url":"https://api.github.com/repos/vuejs/vuejs.org/milestones{/number}","notifications_url":"https://api.github.com/repos/vuejs/vuejs.org/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vuejs/vuejs.org/labels{/name}","releases_url":"https://api.github.com/repos/vuejs/vuejs.org/releases{/id}","deployments_url":"https://api.github.com/repos/vuejs/vuejs.org/deployments","created_at":"2014-01-05T03:11:14Z","updated_at":"2017-09-13T02:48:44Z","pushed_at":"2017-09-13T05:04:34Z","git_url":"git://github.com/vuejs/vuejs.org.git","ssh_url":"git@github.com:vuejs/vuejs.org.git","clone_url":"https://github.com/vuejs/vuejs.org.git","svn_url":"https://github.com/vuejs/vuejs.org","homepage":"vuejs.org","size":36102,"stargazers_count":1182,"watchers_count":1182,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":1327,"mirror_url":null,"open_issues_count":62,"forks":1327,"open_issues":62,"watchers":1182,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681"},"html":{"href":"https://github.com/vuejs/vuejs.org/pull/681"},"issue":{"href":"https://api.github.com/repos/vuejs/vuejs.org/issues/681"},"comments":{"href":"https://api.github.com/repos/vuejs/vuejs.org/issues/681/comments"},"review_comments":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681/comments"},"review_comment":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/vuejs/vuejs.org/pulls/681/commits"},"statuses":{"href":"https://api.github.com/repos/vuejs/vuejs.org/statuses/7aed35bf634483b0c1fae0d6c6defdbd845cecec"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"LucasLeandro1204"}}