121

I have a text input. When the input receives focus I want to select the text inside of the input.

With jQuery I'd do it this way:

<input type="text" value="test" />
$("input[type=text]").click(function() {
    $(this).select();
    // would select "test" in this example
});

I've searched around to try and find the Angular way but most examples I'm finding are dealing with a directive that is watching a modal property for a change. I'm assuming I need a directive that is watching for an input that receives focus. How would I do that?

Blackhole
  • 20,129
  • 7
  • 70
  • 68
Billy Coover
  • 3,827
  • 5
  • 36
  • 50
  • I understand you want to find an 'Angular way', however is there any reason you wouldn't just do ``? – Josef P. Mar 31 '17 at 11:42

11 Answers11

217

The way to do this in Angular is to create a custom directive which does the autoselect for you.

module.directive('selectOnClick', ['$window', function ($window) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('click', function () {
                if (!$window.getSelection().toString()) {
                    // Required for mobile Safari
                    this.setSelectionRange(0, this.value.length)
                }
            });
        }
    };
}]);

Apply the directive like this:

<input type="text" value="test" select-on-click />

View demo

Update1: Removed jQuery dependency.

Update2: Restrict as attribute.

Update3: Works in mobile Safari. Allows selecting part of the text (requires IE>8).

Martin
  • 8,876
  • 2
  • 35
  • 36
  • 10
    If you want to do this without jquery, change element.click to element.bind('click', function() { ... } and element.select() to element[0].select() – jack Aug 15 '13 at 07:02
  • 3
    Nice and elegant. I would also explicitly restrict the directive to be applied as attribute (by returning an object literal and setting `restrict: 'A'`), as this directive aims for *extending the behavior of an existing element*. – Eliran Malka Feb 09 '14 at 10:02
  • @Martin any idea how to do that on page open, without user click? The problem is that default value is set in another controller before my `selectOnLoad` directive link(). But in link(), although scope is already populated, element DOM is not yet synced with $scope, so when I call select(), element is still empty and nothing selected. The only way to work around that I found, is $timeout, but I feel that can stop working with some new Angular version. – Dzmitry Lazerka Jan 10 '15 at 08:11
  • this works well on desktop/android but when I try on ipad it doesn't seem to work. Is there a known work around here? – ak85 Mar 01 '15 at 05:52
  • 2
    @ak85 [apparently mobile Safari needs some slightly different javascript](http://stackoverflow.com/questions/4067469/selecting-all-text-in-html-text-input-when-clicked/4067488#4067488) – Abraham Chan May 01 '15 at 03:47
  • This seems the best way to go, but unfortunately it prevents selecting part of the text. – user1338062 Jun 04 '15 at 11:07
  • I still had significant issues getting this to work on mobile safari. For one, the sample code only listens for "click" events and on mobile safari, the events are "touchstart" and "touchend". I also had issues with the automatic focusing causing the selection to end, so I had to prevent default behavior on touchend. Here's my modified version of the directive. https://gist.github.com/infomofo/4e9828fbd886eeb226a8 – infomofo Jan 12 '16 at 18:55
  • Note that this won't work on ``, see: http://stackoverflow.com/questions/21177489/selectionstart-selectionend-on-input-type-number-no-longer-allowed-in-chrome – Adam Reis Jul 14 '16 at 03:47
44

Here's an enhanced directive which avoids reselecting the text when the user clicks to position the cursor in the input box.

module.directive('selectOnClick', function () {
    return {
        restrict: 'A',
        link: function (scope, element) {
            var focusedElement;
            element.on('click', function () {
                if (focusedElement != this) {
                    this.select();
                    focusedElement = this;
                }
            });
            element.on('blur', function () {
                focusedElement = null;
            });
        }
    };
})
Tamlyn
  • 22,122
  • 12
  • 111
  • 127
  • 3
    I think this answer is better than the accepted, because it allow set cursor to some position in entered text, but, as a directive have own scope - i propose to rename **focusedElement** variable to **isElementFocused** and store there _true_ or _false_ – Vladimir Gordienko Oct 06 '14 at 12:55
  • 2
    I get Uncaught TypeError: undefined is not a function on "this" for .select(); and yes jQuery 1.9.1 is included. – Robbie Smith Jan 12 '15 at 19:57
  • @RobbieSmith I'm 3 years late, but change all instances of `this` to `element[0]` and it should work. I also recommend changing `click` to `focus` so that text will be selected if the user tabs into the input instead of clicking into it. – Lex Feb 01 '18 at 22:06
41

This is an old answer, for Angular 1.x

Better way to do it is by using the ng-click built-in directive:

<input type="text" ng-model="content" ng-click="$event.target.select()" />

EDIT:

As JoshMB has kindly reminded; referencing DOM nodes in Angular 1.2+ is no longer allowed. So, I've moved $event.target.select() into the controller:

<input type="text" ng-model="content" ng-click="onTextClick($event)" />

Then in your controller:

$scope.onTextClick = function ($event) {
    $event.target.select();
};

Here is an example fiddle.

Onur Yıldırım
  • 32,327
  • 12
  • 84
  • 98
  • 2
    As far as I can tell, this no longer supported. Angular throws an error: "Referencing DOM nodes in Angular expressions is disallowed!". See this thread for more info: https://groups.google.com/forum/#!topic/angular/bsTbZ86WAY4. The directive approach works well, however. – JoshMB Apr 17 '14 at 20:17
  • You're right. This is the case for Angular 1.2+. I'll update the answer. – Onur Yıldırım May 10 '14 at 21:37
  • 2
    DOM changes should be done in the directive, or not? – Piioo Oct 28 '14 at 08:45
  • For some reason the other solutions didn't work for me but this did. I think the problem was that I already had a directive on the element which I was adding this click event to. – Precastic Jan 08 '15 at 06:32
  • Seriously, even the onTextClick($event) is throwing the error now. Aside from the un-intuitive nature of this beast, how much is this inconvenience costing us in performance? – jcalfee314 Feb 15 '15 at 23:58
  • I'm giving up .. my angularjs app is going to be harder to use. – jcalfee314 Feb 15 '15 at 23:59
  • Using `ng-focus` allows a user to select subsets if they really want to. – mtfurlan Apr 21 '17 at 16:36
  • On angular 6 it's almost the same: Only replace ng-click with (click)="onTextClick($event); and in Your component onTexclick = function($event) { ...} – Claudio Ferraro May 30 '19 at 16:50
15

Neither of proposed solutions worked well for me. After quick research I came up with this:

module.directive('selectOnFocus', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var focusedElement = null;

            element.on('focus', function () {
                var self = this;
                if (focusedElement != self) {
                    focusedElement = self;
                    $timeout(function () {
                        self.select();
                    }, 10);
                }
            });

            element.on('blur', function () {
                focusedElement = null;
            });
    }

  }

});

It's a mix of several solutions proposed, but works both on click and focus (think autofocus) and allows manual selection of partial value in the input.

Louis
  • 2,548
  • 10
  • 63
  • 120
xaralis
  • 4,634
  • 1
  • 23
  • 20
12

For me, ng-click didn't have sense, because you could never reposition the cursor without selecting all text again, I found this was annoing. Then it is better to use ng-focus, because the text is selected only if the input was not focused, if you click again to reposition the cursor then the text just deselects, as expected, and you can write in between.

I found this approach to be the expected UX behaviour.

Use ng-focus

Option 1: separate code

<input type="text" ng-model="content" ng-focus="onTextFocus($event)" />

and in controller

$scope.onTextFocus = function ($event) {
  $event.target.select();
};

Option 2: as an alternative you can join the code above into this "one-liner" (I didn't test this but it should work)

<input type="text" ng-model="content" ng-focus="$event.target.select()" />
jperelli
  • 6,988
  • 5
  • 50
  • 85
  • Simple and it works. This also allows for positioning of your cursor within the selected text, as discussed above. – pistol-pete Dec 28 '16 at 17:12
12

No directives needed, just add onfocus="this.select()" native javascript to the input element or text area.

Adam Reis
  • 4,165
  • 1
  • 44
  • 35
8

In angular 2, this worked for me, both in Chrome & Firefox:

<input type="text" (mouseup)="$event.target.select()">
  • In Angular 2 or in a more recent version you are using? I did not specify the browsers' versions at that time: that's a mistake since browsers evolve as well. So the error you mention might be link to more recent versions of Chrome and/or Firefox. Thanks for signaling the issue in all cases. –  Aug 11 '21 at 10:46
  • 1
    I found the issue. Provided the answer above too. – Niamatullah Bakhshi Aug 11 '21 at 11:00
6

The accepted answer is using the click event however if you use the focus event, only the 1st click will fire the event and it also fires when some other method is used to focus on the input (like hitting tab or calling focus in code).

This also makes it unnecessary to check if the element is already focused as Tamlyn's answer suggests.

app.directive('selectOnClick', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('focus', function () {
                this.select();
            });
        }
    };
});
OrangeKing89
  • 694
  • 6
  • 16
  • In Firefox 38.0.5 clicking in the middle of the text first selects all, but then removes the selection, so this won't work. – user1338062 Jun 04 '15 at 10:27
  • 1
    This probably isn't going to ever work consistently without delaying `this.select`. – Langdon Oct 07 '16 at 18:19
3

The easiest way to select all text on click or focus or tab, is !!!:

<input (focus)="inputFocused($event)">

...

inputFocused(event: any){
    event.target.select()
}
Dan Ortega
  • 1,679
  • 17
  • 13
0

Modified version that worked for me. First click selects text, second click on same element deselects text

module.directive('selectOnClick', function ($timeout) {
return {
    restrict: 'A',
    link: function (scope, element, attrs) {
        var prevClickElem = null; //Last clicked element
        var ignorePrevNullOnBlur = false; //Ignoring the prevClickElem = null in Blur massege function

        element.on('click', function () {
            var self = this;
            if (prevClickElem != self) { //First click on new element
                prevClickElem = self; 
                $timeout(function () { //Timer
                    if(self.type == "number")
                        self.select();
                    else
                        self.setSelectionRange(0, self.value.length)
                }, 10);
            }
            else { //Second click on same element
                if (self.type == "number") {
                    ignorePrevNullOnBlur = true;
                    self.blur();
                }
                else
                    self.setSelectionRange(self.value.length, self.value.length); //deselect and set cursor on last pos
            }
        });

        element.on('blur', function () {
            if (ignorePrevNullOnBlur == true)
                ignorePrevNullOnBlur = false; //blur called from code handeling the second click 
            else
                prevClickElem = null; //We are leaving input box
        });
    }
}

})

Ola
  • 1
-1

$event.target.select() not resolved in Angular template

The event target can be any HTML Element and a cast to the application interface should be applied here (like HTMLSelectElement), however, Angular expressions don't allow casting. The best workaround would be to wrap $event.target with $any()

so, it should look like this: (focus) = $any($event.target).select()

further explanation found here here.

Niamatullah Bakhshi
  • 1,445
  • 16
  • 27