¿Cómo prevenir el defecto en las etiquetas de anclaje?


342

Digamos que tengo una etiqueta de anclaje como

<a href="#" ng-click="do()">Click</a>

¿Cómo puedo evitar que el navegador navegue a # en AngularJS ?


14
Como Chris dice a continuación, solo omita el href.
Jesse

13
Eso no es lo que Chris dice, Jesse, usa href=''para mantener el comportamiento del puntero del mouse.
oma

1
Simplemente elimine el signo # de href, está bien.
HENG Vongkol

44
O simplemente use href = "javascript: void (0);" para mantener el puntero pero no realizar ninguna acción (hasta que agregue otro controlador de clic)
Robba

1
¿Podría cambiar la respuesta aceptada a la mejor y más votada por Chris - stackoverflow.com/a/11672909 ?
Piotr Dobrogost

Respuestas:


259

ACTUALIZACIÓN : desde entonces he cambiado de opinión sobre esta solución. Después de más desarrollo y tiempo dedicado a trabajar en esto, creo que una mejor solución a este problema es hacer lo siguiente:

<a ng-click="myFunction()">Click Here</a>

Y luego actualice su csspara tener una regla adicional:

a[ng-click]{
    cursor: pointer;
}

Es mucho más simple y proporciona exactamente la misma funcionalidad y es mucho más eficiente. Espero que pueda ser útil para cualquier otra persona que busque esta solución en el futuro.


La siguiente es mi solución anterior, que dejo aquí solo para fines heredados:

Si tiene mucho este problema, una directiva simple que solucionaría este problema es la siguiente:

app.directive('a', function() {
    return {
        restrict: 'E',
        link: function(scope, elem, attrs) {
            if(attrs.ngClick || attrs.href === '' || attrs.href === '#'){
                elem.on('click', function(e){
                    e.preventDefault();
                });
            }
        }
   };
});

Comprueba todas las etiquetas de anclaje ( <a></a>) para ver si su hrefatributo es una cadena vacía ( "") o un hash ( '#') o si hay una ng-clickasignación. Si encuentra alguna de estas condiciones, detecta el evento y evita el comportamiento predeterminado.

El único inconveniente es que ejecuta esta directiva para todas las etiquetas de anclaje. Entonces, si tiene muchas etiquetas de anclaje en la página y solo desea evitar el comportamiento predeterminado para un pequeño número de ellas, entonces esta directiva no es muy eficiente. Sin embargo, casi siempre quiero hacerlo preventDefault, así que uso esta directiva en todas mis aplicaciones de AngularJS.


2
Gracias @matejkramny, crítica muy constructiva. ¿Alguna sugerencia para hacerlo menos "desordenado"?
tennisgent

1
Como se indicó en otras respuestas, tener un hrefatributo en blanco tiene un comportamiento variable en diferentes navegadores. Aunque estoy de acuerdo en que es, con mucho, la solución más limpia y eficiente, tiene algunos problemas entre navegadores. El uso del enfoque directivo permite que el navegador maneje la <a>etiqueta como lo haría normalmente, pero aún así le da al OP la respuesta que estaba buscando.
tennisgent

2
Esto funciona y todo, pero es demasiado exagerado para un problema muy simple. Angular le da acceso al objeto de evento con $ event, para que pueda hacer exactamente lo que haría en eventos js simples o jquery click. Ver esta respuesta stackoverflow.com/a/19240232/1826354 y mi comentario al respecto
Charlie Martin

1
Sí. Usted puede. Hay otras dos respuestas en esta publicación que ya hacen lo que estás describiendo. Y estoy de acuerdo, es exagerado. Es por eso que hice la actualización que hice.
tennisgent

66
Esto está bien si no le importa la accesibilidad en su sitio. Al dejar el href, no es posible usar su teclado para tabular ese elemento. A menos que haya agregado un índice tabindex. Con todo eso en mente, ¿por qué no usar preventDefault ...? Para eso está ahí.
duhseekoh

319

De acuerdo con los documentos para ngHref , debería poder dejar el href o do href = "".

<input ng-model="value" /><br />
<a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
<a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
<a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
<a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />

66
Es la única buena respuesta real. Cualquier cosa que requiera tocar el DOM o los eventos nativos es cuestionable
Vincent

44
Esta es la respuesta correcta. Si suelta href del atributo <a>, AngularJS llamará a prevent default:
pkozlowski.opensource

25
El único inconveniente de dejar fuera el atributo href es que pierde el cursor 'puntero' normal al pasar el mouse sobre un enlace. Puede solucionar esto agregando una regla a su hoja de estilo: a: hover {cursor: pointer; }
karlgold

35
Tenga en cuenta que esto también hace que la etiqueta no se pueda tabular, lo que no es muy accesible.
Richard Szalay

3
Para mí, el navegador todavía muestra la acción de clic: /
Matej

238

Puede pasar el objeto $ event a su método y llamarlo $event.preventDefault()para que no se produzca el procesamiento predeterminado:

<a href="#" ng-click="do($event)">Click</a>

// then in your controller.do($event) method
$event.preventDefault()

13
sí, y también puede llamar a $ event.stopPropagation () para que el evento no surja (básicamente, tiene acceso al objeto Event tal como lo define el W3C: w3.org/TR/DOM-Level-2- Events / events.html # Events-Event )
mna

1
Tenga en cuenta que en el momento en que esto se escribió, dejar el href vacío (o ausente) no funcionaba en IE8 / 9 y Opera. Esto fue hace un año, y no he tenido la oportunidad de volver a intentarlo (abrí un problema en Github, pero creo que restablecieron el problema hace algún tiempo). Si esto se soluciona (o si no te interesan estos navegadores), entonces, por supuesto, ¡usa la respuesta de Chris! Si alguien puede probarlo y comentar / editar la respuesta, aún mejor.
mna

1
@PuerkitoBio: puedo confirmar que IE8 e inferior aún requieren $event.preventDefault()... impuesto de IE.
Scotty.NET

44
pasar el evento $ al controlador significa hacer referencia al DOM en su controlador, lo que significa que ha acoplado su vista y su controlador. Puede llamar a los métodos $ event directamente en su expresión evaluada: ng-click="do(); $event.preventDefault()por ejemplo ... aunque, no es necesario porque la respuesta de @ Chris a continuación es la correcta. $event.stopPropagation()Sin embargo, esto podría ayudar a las personas en situaciones en las que han anidado ngClicks y desean llamar .
Ben Lesh

2
Finalmente una solución amigable para SEO. Es posible que también desee actualizar la URL del navegador sin volver a cargar ng-view: joelsaupe.com/programming/… De esa manera, tiene enlaces que no recargan su vista, pero que pueden abrirse en una nueva pestaña y los motores de búsqueda pueden seguirlos. ¡HURRA!
Tom

111

Prefiero usar directivas para este tipo de cosas. Aquí hay un ejemplo

<a href="#" ng-click="do()" eat-click>Click Me</a>

Y el código directivo para eat-click:

module.directive('eatClick', function() {
    return function(scope, element, attrs) {
        $(element).click(function(event) {
            event.preventDefault();
        });
    }
})

Ahora puede agregar el eat-clickatributo a cualquier elemento y obtendrápreventDefault() automáticamente.

Beneficios:

  1. No tienes que pasar el $eventobjeto feo a tudo() función.
  2. Su controlador tiene más unidades de prueba porque no necesita apagar el $eventobjeto

1
@Kato Angular viene con su propio subconjunto de jQuery, para hacernos la vida más fácil.
Neil

3
Beneficio adicional : esto también funciona en IE8 y versiones posteriores, si eso es importante para usted.
Scotty.NET

1
Me estoy poniendo Error: $ is not defined. ¿Qué me estoy perdiendo?
Bilal Mirza

77
Probé angular.element(element).bind('click', function(event){...});. Funcionó. Ref: jQLite
Bilal Mirza

3
Presentar una directiva para algo simple como eso parece un poco abultado si me preguntas ... Mira la respuesta de Chris a continuación
Wilt

54

Aunque Renaud dio una gran solución

<a href="#" ng-click="do(); $event.preventDefault()">Click</a> 

Personalmente descubrí que también necesitas $ event.stopPropagation () en algunos casos para evitar algunos de los efectos secundarios

<a href="#" ng-click="do(); $event.preventDefault(); $event.stopPropagation();">
    Click</a>

será mi solución


34
ng-click="$event.preventDefault()"

13
Esta es la mejor solución. Por supuesto, también puede pasar el objeto $ event a su función de alcance. Como ng-click = "myFunction ($ event, otherParams) y luego $ event.preventDefault () en myFunction. Lanzar su propia directiva para esto es excesivo
Charlie Martin

34

La solución más fácil que he encontrado es esta:

<a href="#" ng-click="do(); $event.preventDefault()">Click</a>

Esto no solo es lo más fácil, sino que tampoco combina eventos con el controlador como se sugiere en otras soluciones. El acoplamiento de eventos a un controlador es algo que debe evitar a toda costa, ya que dificulta mucho las pruebas.
jornare

11

Entonces, leyendo estas respuestas, @Chris todavía tiene la respuesta más "correcta", supongo, pero tiene un problema, no muestra el "puntero" ...

Aquí hay dos formas de resolver este problema sin necesidad de agregar un cursor: estilo de puntero:

  1. Usar en javascript:void(0)lugar de #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. Úselo $event.preventDefault()en la ng-clickdirectiva (para no desechar su controlador con referencias relacionadas con DOM):

    <a href="#dontGoHere" ng-click="doSomething(); $event.preventDefault()">Do Something</a>

Personalmente prefiero el primero sobre el segundo. javascript:void(0)tiene otros beneficios que se discuten aquí . También hay una discusión sobre "JavaScript discreto" en ese enlace que es terriblemente reciente y no necesariamente se aplica directamente a una aplicación angular.


2
¿Por qué se rechaza esto? También noté que el puntero no se muestra con la respuesta de Chris.
Wim Deblauwe

2
javascript: void (0) es mucho mejor que #, que puede actuar en la ruta si lo configura mal. +1
Shay Elkayam

2
Estaba a punto de escribir una respuesta a esto. También puedes hacerlojavascript:;
Adrian el

10

Puedes hacer lo siguiente

1.Eliminar hrefatributo de la aetiqueta anchor ( )

2.Ajuste el cursor del puntero en CSS a ng elementos de clic

 [ng-click],
 [data-ng-click],
 [x-ng-click] {
     cursor: pointer;
 }

9

HTML

aquí angularjs puro: cerca de la función ng-click puede escribir la función preventDefault () separando el punto y coma

<a href="#" ng-click="do(); $event.preventDefault(); $event.stopPropagation();">Click me</a>

JS

$scope.do = function() {
    alert("do here anything..");
}

(o)

puedes proceder de esta manera, esto ya se discutió aquí.

HTML

<a href="#" ng-click="do()">Click me</a>

JS

$scope.do = function(event) {
    event.preventDefault();
    event.stopPropagation()
}

7

Pruebe esta opción, que puedo ver, aún no figura en la lista anterior:

<a href="" ng-click="do()">Click</a>

6

Ya que está haciendo una aplicación web, ¿por qué necesita enlaces?

¡Cambia tus anclas por botones!

<button ng-click="do()"></button>

2
¿Desde cuándo las webapps no pueden tener anclas?
Robin van Baalen

1
Me refería al punto de que la mayoría de las aplicaciones web no necesitan enlaces relativos como los sitios web, a menudo también usan enlaces de anclaje como activadores de JavaScript y no enlazan a una página; por lo tanto, un botón con el reinicio de estilo predeterminado es tan semánticamente correcto, si no más.
sidonaldson

3
@sidonaldson En realidad, muchas aplicaciones web actualmente dependen en gran medida de los enlaces. Mira el enrutamiento angularjs.
Robert

1
@Robert sí, pero si usa el enrutamiento integrado no tiene que administrar evitar el valor predeterminado, lo hace por usted. Supongo que dado que el póster quiere cancelar un enlace de anclaje, está hablando de enlaces que no son de ruta. Por ejemplo, lanzar un modal, etc.
sidonaldson

2
Esta respuesta debería tener muchos más votos a favor. En muchos casos, si desea evitar el comportamiento de anclaje predeterminado, está utilizando el ancla como un botón y no como un enlace.
ItsCosmo

6

si aún es relevante:

<a ng-click="unselect($event)" />

...

scope.unselect = function( event ) {
 event.preventDefault();
 event.stopPropagation();
}

...

6

La forma más segura de evitar eventos en un href sería definirlo como

<a href="javascript:void(0)" ....>

5

Yo iría con:

<a ng-click="do()">Click</a>
  • porque de acuerdo con los documentos, deberías poder salir del href y luego Angular se encargará de evitar el incumplimiento por ti.

Todo esto impide que lo predeterminado me haya resultado confuso, por lo que he creado un JSFiddle para ilustrar cuándo y dónde Angular está evitando el incumplimiento .

El JSFiddle está utilizando Angular es una directiva, por lo que debe ser EXACTAMENTE igual. Puede ver el código fuente aquí: un código fuente de etiqueta

Espero que esto ayude a aclarar algunos.

Me hubiera gustado publicar el documento en ngHref pero no puedo debido a mi reputación.


5

Esto es lo que siempre hago. ¡Funciona de maravilla!

<a href ng-click="do()">Click</a>

4

O si necesita en línea, puede hacer esto:

<a href="#" ng-click="show = !show; $event.preventDefault()">Click to show</a>

4

Muchas respuestas parecen ser excesivas. PARA MÍ, esto funciona

 <a ng-href="#" ng-click="$event.preventDefault();vm.questionContainer($index)">{{question.Verbiage}}</a>

2

Necesito una presencia del valor del atributo href para la degradación (cuando js está apagado), por lo que no puedo usar el atributo href vacío (o "#"), pero el código anterior no funcionó para mí, porque necesito un evento ( e) variable. Creé mi propia directiva:

angular.module('MyApp').directive('clickPrevent', function() {
  return function(scope, element, attrs) {
    return element.on('click', function(e) {
      return e.preventDefault();
    });
  };
});

En HTML:

<a data-click-prevent="true" href="/users/sign_up" ng-click="openSignUpModal()">Sign up</a>

2

Tomando prestado de la respuesta de tennisgent. Me gusta que no tenga que crear una directiva personalizada para agregar todos los enlaces. Sin embargo, no pude conseguir que trabajara en IE8. Esto es lo que finalmente funcionó para mí (usando angular 1.0.6).

Tenga en cuenta que 'bind' le permite usar jqLite proporcionado por angular, por lo que no es necesario envolverlo con jQuery completo. También se requiere el método stopPropogation.

.directive('a', [
    function() {
        return {
            restrict: 'E',
            link: function(scope, elem, attrs) {

                elem.bind('click', function(e){
                    if (attrs.ngClick || attrs.href === '' || attrs.href == '#'){
                        e.preventDefault();
                        e.stopPropagation();
                    }
                })
            }
        };
    }
])

3
Esto mata demasiado. Por ejemplo, cuando uso Twitter Bootstrap's dropdown, quiero la propagación pero no el comportamiento predeterminado. Este fragmento NO se recomienda.
Robin van Baalen

2

Me encontré con este mismo problema al usar los anclajes para un menú desplegable angular. La única solución que encontré que evitó los efectos secundarios no deseados (es decir, que el menú desplegable no se cerró debido al uso de preventDefault ()) fue usar lo siguiente:

 <a href="javascript:;" ng-click="do()">Click</a>

2

si nunca quiere ir a href ... debe cambiar su marcado y usar un botón, no una etiqueta de ancla porque semánticamente no es un ancla o un enlace. Inversamente, si tiene un botón que realiza algunas comprobaciones y luego lo redirige, debe ser una etiqueta de enlace / ancla y no un botón ... para fines de SEO y pruebas [inserte aquí el paquete de pruebas].

para los enlaces que solo desea redirigir condicionalmente, como solicitar que se guarden los cambios, o reconocer el estado sucio ... debe usar ng-click = "someFunction ($ event)" y en someFunction en su validationError preventDefault y / o stopPropagation.

También sólo porque puede no significa que debe . javascript: void (0) funcionó bien en el pasado ... pero debido a los nefastos hackers / crackers, los navegadores señalan aplicaciones / sitios que usan javascript dentro de un href ... no veo razón para ducktype arriba ...

es 2016, ya que 2010 no debería ser motivo para usar enlaces que actúen como botones ... si aún tiene que admitir IE8 y, a continuación, debe reevaluar su lugar de trabajo.


1
/* NG CLICK PREVENT DEFAULT */

app.directive('ngClick', function () {
    return {
        link: function (scope, element, attributes) {
            element.click(function (event) {
                event.preventDefault();
                event.stopPropagation();
            });
        }
    };
});

1

Lo que debe hacer es omitir el hrefatributo por completo.

Si observa el código fuente de la adirectiva del elemento (que es parte del núcleo angular), se indica en la línea 29 - 31:

if (!element.attr(href)) {
    event.preventDefault();
}

Lo que significa que Angular ya está resolviendo el problema de los enlaces sin href. El único problema que aún tiene es el problema de CSS. Todavía puede aplicar el estilo de puntero a los anclajes que tienen ng-clicks, por ejemplo:

a[ng-click] {
    /* Styles for anchors without href but WITH ng-click */
    cursor: pointer;
}

Por lo tanto, incluso podría hacer que su sitio sea más accesible marcando enlaces reales con un estilo sutil y diferente, luego enlaces que realizan funciones.

¡Feliz codificación!


1

Puede deshabilitar la redirección de URL dentro del Html5Mode de $ location para lograr el objetivo. Puede definirlo dentro del controlador específico utilizado para la página. Algo como esto:

app.config(['$locationProvider', function ($locationProvider) {
    $locationProvider.html5Mode({
        enabled: true,
        rewriteLinks: false,
        requireBase: false
    });
}]);


1

Si está utilizando Angular 8 o más, puede crear una directiva:

import { Directive, HostListener } from '@angular/core';

@Directive({
  selector: '[preventDefault]'
})
export class PreventDefaultDirective {

  @HostListener("click", ["$event"])
  public onClick(event: any): void
  {
    console.log('click');
    debugger;
      event.preventDefault();
  }

}

En su etiqueta de anclaje en el componente, puede cablearlo así:

  <a ngbDropdownToggle preventDefault class="nav-link dropdown-toggle" href="#" aria-expanded="false" aria-haspopup="true" id="nav-dropdown-2">Pages</a>

El módulo de aplicación debe tener su declaración:

import { PreventDefaultDirective } from './shared/directives/preventdefault.directive';


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective

-1

Una alternativa podría ser:

<span ng-click="do()">Click</span>

1
Es una práctica horrible abusar de un spanpara hacer clic. Simplemente busque la respuesta de @ tennisgent: anclas sin hrefdefinir.
Robin van Baalen


1
Si haces <span> Hola </span> ¿desde cuándo se puede hacer clic? Lo que la especificación probablemente dice es que se puede usar un <span> dentro de un elemento en el que se puede hacer clic, como <a> o <button>. Pero, de nuevo, todos los elementos de bloque en línea (img, span, etc.) se pueden hacer clic de esa manera. Un lapso en sí mismo no es cliqueable.
Robin van Baalen

1
@RobinvanBaalen Por favor vea los enlaces de arriba. El atributo onclick para elementos span se ha definido desde al menos HTML 4.01.
Terence Bandoian

1
Nunca dije que agregar un controlador de eventos no funcionaría por un lapso. Dije que es una mala práctica hacer que un elemento no cliqueable, como un span, div, section, ul, li, etc., sea cliqueable utilizando un controlador de eventos javascript. Se trata de semántica. La semántica dentro de HTML es la práctica de dar contenido y estructura a la página mediante el uso del elemento adecuado.
Robin van Baalen
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.