Seleccionar texto en foco de entrada

121

Tengo una entrada de texto. Cuando la entrada recibe el foco, quiero seleccionar el texto dentro de la entrada.

Con jQuery lo haría de esta manera:

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

He buscado alrededor para tratar de encontrar la forma Angular, pero la mayoría de los ejemplos que estoy encontrando están tratando con una directiva que está viendo una propiedad modal para un cambio. Supongo que necesito una directiva que esté pendiente de una entrada que reciba atención. ¿Como podría hacerlo?

Billy Coover
fuente
Entiendo que quieres encontrar una "forma angular", pero ¿hay alguna razón por la que no lo harías <input type="text" value="test" onclick="this.select()" />?
Josef P.

Respuestas:

216

La forma de hacer esto en Angular es crear una directiva personalizada que haga la selección automática por usted.

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)
                }
            });
        }
    };
}]);

Aplica la directiva así:

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

View demo

Actualización 1 : Se eliminó la dependencia de jQuery.

Actualización2 : Restringir como atributo.

Actualización3 : Funciona en Safari móvil. Permite seleccionar parte del texto (requiere IE> 8).

Martín
fuente
10
Si desea hacer esto sin jquery, cambie element.click a element.bind ('click', function () {...} y element.select () a element [0] .select ()
jack
3
Agradable y elegante También restringiría explícitamente la directiva para que se aplique como atributo (devolviendo un objeto literal y configuración restrict: 'A'), ya que esta directiva tiene como objetivo extender el comportamiento de un elemento existente .
Eliran Malka
@Martin alguna idea de cómo hacer eso en la página abierta, sin clic del usuario? El problema es que el valor predeterminado se establece en otro controlador antes de mi selectOnLoadenlace de directiva (). Pero en link (), aunque el alcance ya está poblado, el elemento DOM aún no está sincronizado con $ scope, por lo que cuando llamo select (), el elemento todavía está vacío y no se selecciona nada. La única forma de evitar esto que encontré es $ timeout, pero creo que puede dejar de funcionar con alguna nueva versión angular.
Dzmitry Lazerka
esto funciona bien en computadoras de escritorio / android, pero cuando pruebo ipad no parece funcionar. ¿Hay algún trabajo conocido por aquí?
ak85
44

Aquí hay una directiva mejorada que evita volver a seleccionar el texto cuando el usuario hace clic para colocar el cursor en el cuadro de entrada.

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
fuente
3
Creo que esta respuesta es mejor que la aceptada, porque permite establecer el cursor en alguna posición en el texto ingresado, pero, como directiva tiene su propio alcance: propongo cambiar el nombre de la variable focusElement a isElementFocused y almacenar allí verdadero o falso
Vladimir Gordienko
2
Obtengo TypeError no capturado: undefined no es una función en "this" para .select (); y sí, jQuery 1.9.1 está incluido.
Robbie Smith
@RobbieSmith Llego 3 años tarde, pero cambio todas las instancias de thisa element[0]y debería funcionar. También recomiendo cambiar clicka focuspara que el texto se seleccione si el usuario tabula la entrada en lugar de hacer clic en ella.
Lex
40

Esta es una vieja respuesta, para Angular 1.x

La mejor manera de hacerlo es utilizando la ng-clickdirectiva integrada:

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

EDITAR:

Como JoshMB ha recordado amablemente; ya no se permite hacer referencia a nodos DOM en Angular 1.2+. Entonces, me mudé $event.target.select()al controlador:

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

Luego en su controlador:

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

Aquí hay un ejemplo de violín .

Onur Yıldırım
fuente
2
Por lo que puedo decir, esto ya no es compatible. Angular arroja un error: "¡No se permite hacer referencia a nodos DOM en expresiones angulares!". Consulte este hilo para obtener más información: groups.google.com/forum/#!topic/angular/bsTbZ86WAY4 . Sin embargo, el enfoque directivo funciona bien.
JoshMB
Tienes razón. Este es el caso de Angular 1.2+. Actualizaré la respuesta.
Onur Yıldırım
2
Los cambios de DOM deben hacerse en la directiva, ¿o no?
Piioo
Por alguna razón, las otras soluciones no funcionaron para mí, pero esto sí. Creo que el problema era que ya tenía una directiva sobre el elemento al que estaba agregando este evento de clic.
Precastic
En serio, incluso el onTextClick ($ event) está arrojando el error ahora. Además de la naturaleza no intuitiva de esta bestia, ¿cuánto nos cuesta este inconveniente en el rendimiento?
jcalfee314
15

Ninguna de las soluciones propuestas funcionó bien para mí. Después de una investigación rápida, se me ocurrió esto:

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;
            });
    }

  }

});

Es una combinación de varias soluciones propuestas, pero funciona tanto en clic como en enfoque (piense en el enfoque automático) y permite la selección manual de valor parcial en la entrada.

xaralis
fuente
1
Error de sintaxis corregido: último}); sustituir por: } }; });
Blackfire
Este funciona para input type = "number", ¡lo cual es genial! Gracias
Louis
Oye, esto funciona para Ionic en Android, pero no en iOS ... ¿alguna idea de por qué? Gracias
Louis
2
Parece que dejaste algo de código cuando lo adaptaste de onClick ... ¿para qué sirve focusedElement?
Langdon
12

Para mí, ng-click no tenía sentido, porque nunca podías reposicionar el cursor sin seleccionar todo el texto nuevamente, encontré que esto era molesto. Entonces es mejor usar ng-focus, porque el texto se selecciona solo si la entrada no estaba enfocada, si hace clic nuevamente para reposicionar el cursor, el texto simplemente deselecciona, como se esperaba, y puede escribir en el medio.

Encontré que este enfoque es el comportamiento UX esperado.

Use ng-focus

Opción 1: código separado

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

y en controlador

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

Opción 2: como alternativa, puede unir el código anterior en este "one-liner" (no probé esto pero debería funcionar)

<input type="text" ng-model="content" ng-focus="$event.target.select()" />
jperelli
fuente
Simple y funciona. Esto también permite colocar el cursor dentro del texto seleccionado, como se discutió anteriormente.
pistola-pete
7

En angular 2, esto funcionó para mí, tanto en Chrome como en Firefox:

<input type="text" (mouseup)="$event.target.select()">
guenam
fuente
6

La respuesta aceptada es usar el evento de clic; sin embargo, si usa el evento de enfoque, solo el primer clic activará el evento y también se disparará cuando se use algún otro método para enfocarse en la entrada (como presionar la pestaña o llamar al foco en el código).

Esto también hace innecesario verificar si el elemento ya está enfocado como sugiere la respuesta de Tamlyn.

app.directive('selectOnClick', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('focus', function () {
                this.select();
            });
        }
    };
});
OrangeKing89
fuente
En Firefox 38.0.5, hacer clic en el medio del texto primero selecciona todo, pero luego elimina la selección, por lo que esto no funcionará.
user1338062
1
Esto probablemente nunca funcionará de manera consistente sin demora this.select.
Langdon
6

No se necesitan directivas, solo agregue onfocus="this.select()"JavaScript nativo al elemento de entrada o área de texto.

Adam Reis
fuente
1
¿Cómo es que tanta gente tiene todas estas respuestas complejas, cuando la forma más fácil es aquí: D Gracias Adam
SwissCoder
0

Versión modificada que funcionó para mí. El primer clic selecciona el texto, el segundo clic en el mismo elemento deselecciona el texto

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
fuente