Using regular expressions is probably the best way. You can see a bunch of tests here (taken from chromium)
function validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(String(email).toLowerCase());
}
Here's the example of regular expresion that accepts unicode:
const re = /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
But keep in mind that one should not rely only upon JavaScript validation. JavaScript can easily be disabled. This should be validated on the server side as well.
Here's an example of the above in action:
function validateEmail(email) {
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
function validate() {
const $result = $("#result");
const email = $("#email").val();
$result.text("");
if (validateEmail(email)) {
$result.text(email + " is valid :)");
$result.css("color", "green");
} else {
$result.text(email + " is not valid :(");
$result.css("color", "red");
}
return false;
}
$("#email").on("input", validate);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label for=email>Enter an email address:</label>
<input id="email">
<h2 id="result"></h2>
A closure is a pairing of:
- A function, and
- A reference to that function's outer scope (lexical environment)
A lexical environment is part of every execution context (stack frame) and is a map between identifiers (ie. local variable names) and values.
Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.
If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.
In the following code, inner
forms a closure with the lexical environment of the execution context created when foo
is invoked, closing over variable secret
:
function foo() {
const secret = Math.trunc(Math.random()*100)
return function inner() {
console.log(`The secret number is ${secret}.`)
}
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`
In other words: in JavaScript, functions carry a reference to a private "box of state", to which only they (and any other functions declared within the same lexical environment) have access. This box of the state is invisible to the caller of the function, delivering an excellent mechanism for data-hiding and encapsulation.
And remember: functions in JavaScript can be passed around like variables (first-class functions), meaning these pairings of functionality and state can be passed around your program: similar to how you might pass an instance of a class around in C++.
If JavaScript did not have closures, then more states would have to be passed between functions explicitly, making parameter lists longer and code noisier.
So, if you want a function to always have access to a private piece of state, you can use a closure.
...and frequently we do want to associate the state with a function. For example, in Java or C++, when you add a private instance variable and a method to a class, you are associating state with functionality.
In C and most other common languages, after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed. In JavaScript, if you declare a function within another function, then the local variables of the outer function can remain accessible after returning from it. In this way, in the code above, secret
remains available to the function object inner
, after it has been returned from foo
.
Uses of Closures
Closures are useful whenever you need a private state associated with a function. This is a very common scenario - and remember: JavaScript did not have a class syntax until 2015, and it still does not have a private field syntax. Closures meet this need.
Private Instance Variables
In the following code, the function toString
closes over the details of the car.
function Car(manufacturer, model, year, color) {
return {
toString() {
return `${manufacturer} ${model} (${year}, ${color})`
}
}
}
const car = new Car('Aston Martin','V8 Vantage','2012','Quantum Silver')
console.log(car.toString())
Functional Programming
In the following code, the function inner
closes over both fn
and args
.
function curry(fn) {
const args = []
return function inner(arg) {
if(args.length === fn.length) return fn(...args)
args.push(arg)
return inner
}
}
function add(a, b) {
return a + b
}
const curriedAdd = curry(add)
console.log(curriedAdd(2)(3)()) // 5
Event-Oriented Programming
In the following code, function onClick
closes over variable BACKGROUND_COLOR
.
const $ = document.querySelector.bind(document)
const BACKGROUND_COLOR = 'rgba(200,200,242,1)'
function onClick() {
$('body').style.background = BACKGROUND_COLOR
}
$('button').addEventListener('click', onClick)
<button>Set background color</button>
Modularization
In the following example, all the implementation details are hidden inside an immediately executed function expression. The functions tick
and toString
close over the private state and functions they need to complete their work. Closures have enabled us to modularise and encapsulate our code.
let namespace = {};
(function foo(n) {
let numbers = []
function format(n) {
return Math.trunc(n)
}
function tick() {
numbers.push(Math.random() * 100)
}
function toString() {
return numbers.map(format)
}
n.counter = {
tick,
toString
}
}(namespace))
const counter = namespace.counter
counter.tick()
counter.tick()
console.log(counter.toString())
Examples
Example 1
This example shows that the local variables are not copied in the closure: the closure maintains a reference to the original variables themselves. It is as though the stack-frame stays alive in memory even after the outer function exits.
function foo() {
let x = 42
let inner = function() { console.log(x) }
x = x+1
return inner
}
var f = foo()
f() // logs 43
Example 2
In the following code, three methods log
, increment
, and update
all close over the same lexical environment.
And every time createObject
is called, a new execution context (stack frame) is created and a completely new variable x
, and a new set of functions (log
etc.) are created, that close over this new variable.
function createObject() {
let x = 42;
return {
log() { console.log(x) },
increment() { x++ },
update(value) { x = value }
}
}
const o = createObject()
o.increment()
o.log() // 43
o.update(5)
o.log() // 5
const p = createObject()
p.log() // 42
Example 3
If you are using variables declared using var
, be careful you understand which variable you are closing over. Variables declared using var
are hoisted. This is much less of a problem in modern JavaScript due to the introduction of let
and const
.
In the following code, each time around the loop, a new function inner
is created, which closes over i
. But because var i
is hoisted outside the loop, all of these inner functions close over the same variable, meaning that the final value of i
(3) is printed, three times.
function foo() {
var result = []
for (var i = 0; i < 3; i++) {
result.push(function inner() { console.log(i) } )
}
return result
}
const result = foo()
// The following will print `3`, three times...
for (var i = 0; i < 3; i++) {
result[i]()
}
Final points:
- Whenever a function is declared in JavaScript closure is created.
- Returning a
function
from inside another function is the classic example of closure, because the state inside the outer function is implicitly available to the returned inner function, even after the outer function has completed execution.
- Whenever you use
eval()
inside a function, a closure is used. The text you eval
can reference local variables of the function, and in the non-strict mode, you can even create new local variables by using eval('var foo = …')
.
- When you use
new Function(…)
(the Function constructor) inside a function, it does not close over its lexical environment: it closes over the global context instead. The new function cannot reference the local variables of the outer function.
- A closure in JavaScript is like keeping a reference (NOT a copy) to the scope at the point of function declaration, which in turn keeps a reference to its outer scope, and so on, all the way to the global object at the top of the scope chain.
- A closure is created when a function is declared; this closure is used to configure the execution context when the function is invoked.
- A new set of local variables is created every time a function is called.
Links
Best Solution
Overview
There are three primary browser APIs for copying to the clipboard:
Async Clipboard API
[navigator.clipboard.writeText]
document.execCommand('copy')
(deprecated) 👎Overriding the copy event
General development notes
Don't expect clipboard related commands to work whilst you are testing code in the console. Generally, the page is required to be active (Async Clipboard API) or requires user interaction (e.g. a user click) to allow (
document.execCommand('copy')
) to access the clipboard see below for more detail.IMPORTANT (noted here 2020/02/20)
Note that since this post was originally written deprecation of permissions in cross-origin IFRAMEs and other IFRAME "sandboxing" prevents the embedded demos "Run code snippet" buttons and "codepen.io example" from working in some browsers (including Chrome and Microsoft Edge).
To develop create your own web page, serve that page over an HTTPS connection to test and develop against.
Here is a test/demo page which demonstrates the code working: https://deanmarktaylor.github.io/clipboard-test/
Async + Fallback
Due to the level of browser support for the new Async Clipboard API, you will likely want to fall back to the
document.execCommand('copy')
method to get good browser coverage.Here is a simple example (may not work embedded in this site, read "important" note above):
(codepen.io example may not work, read "important" note above) Note that this snippet is not working well in Stack Overflow's embedded preview you can try it here: https://codepen.io/DeanMarkTaylor/pen/RMRaJX?editors=1011
Async Clipboard API
Note that there is an ability to "request permission" and test for access to the clipboard via the permissions API in Chrome 66.
document.execCommand('copy')
The rest of this post goes into the nuances and detail of the
document.execCommand('copy')
API.Browser Support
The JavaScript(deprecated) 👎document.execCommand('copy')
support has grown, see the links below for browser updates:Simple Example
(may not work embedded in this site, read "important" note above)
Complex Example: Copy to clipboard without displaying input
The above simple example works great if there is a
textarea
orinput
element visible on the screen.In some cases, you might wish to copy text to the clipboard without displaying an
input
/textarea
element. This is one example of a way to work around this (basically insert an element, copy to clipboard, remove element):Tested with Google Chrome 44, Firefox 42.0a1, and Internet Explorer 11.0.8600.17814.
(may not work embedded in this site, read "important" note above)
Additional notes
Only works if the user takes an action
All
document.execCommand('copy')
calls must take place as a direct result of a user action, e.g. click event handler. This is a measure to prevent messing with the user's clipboard when they don't expect it.See the Google Developers post here for more info.
Clipboard API
Note the full Clipboard API draft specification can be found here: https://w3c.github.io/clipboard-apis/
Is it supported?
document.queryCommandSupported('copy')
should returntrue
if the command "is supported by the browser".document.queryCommandEnabled('copy')
returntrue
if thedocument.execCommand('copy')
will succeed if called now. Checking to ensure the command was called from a user-initiated thread and other requirements are met.However, as an example of browser compatibility issues, Google Chrome from ~April to ~October 2015 only returned
true
fromdocument.queryCommandSupported('copy')
if the command was called from a user-initiated thread.Note compatibility detail below.
Browser Compatibility Detail
Whilst a simple call to
document.execCommand('copy')
wrapped in atry
/catch
block called as a result of a user click will get you the most compatibility use the following has some provisos:Any call to
document.execCommand
,document.queryCommandSupported
ordocument.queryCommandEnabled
should be wrapped in atry
/catch
block.Different browser implementations and browser versions throw differing types of exceptions when called instead of returning
false
.Different browser implementations are still in flux and the Clipboard API is still in draft, so remember to do your testing.