Comment éviter les défauts sur les balises d'ancrage?


342

Disons que j'ai une balise d'ancrage telle que

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

Comment puis-je empêcher le navigateur de naviguer vers # dans AngularJS ?


14
Comme Chris le dit ci-dessous, laissez de côté le href.
Jesse

13
Ce n'est pas ce que dit Chris, Jesse, href=''pour garder le comportement du pointeur de la souris.
oma

1
Vous venez de supprimer le signe # de href, c'est OK.
HENG Vongkol

4
Ou utilisez simplement href = "javascript: void (0);" pour garder le pointeur mais sans action (jusqu'à ce que vous ajoutiez un autre gestionnaire de clic)
Robba

1
Pourriez-vous s'il vous plaît changer la réponse acceptée à la meilleure et la plus votée par Chris - stackoverflow.com/a/11672909 ?
Piotr Dobrogost

Réponses:


259

MISE À JOUR : J'ai depuis changé d'avis sur cette solution. Après plus de développement et de temps passé à travailler sur cela, je pense qu'une meilleure solution à ce problème est de procéder comme suit:

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

Et puis mettez cssà jour votre pour avoir une règle supplémentaire:

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

C'est beaucoup plus simple et offre exactement la même fonctionnalité et est beaucoup plus efficace. J'espère que cela pourrait être utile à toute personne cherchant cette solution à l'avenir.


Ce qui suit est ma solution précédente, que je laisse ici juste à des fins héritées:

Si vous rencontrez souvent ce problème, une simple directive qui permettrait de résoudre ce problème est la suivante:

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

Il vérifie toutes les balises d'ancrage ( <a></a>) pour voir si leur hrefattribut est soit une chaîne vide ( "") soit un hachage ( '#') ou s'il y a une ng-clickaffectation. S'il trouve l'une de ces conditions, il intercepte l'événement et empêche le comportement par défaut.

Le seul inconvénient est qu'il exécute cette directive pour toutes les balises d'ancrage. Donc, si vous avez beaucoup de balises d'ancrage sur la page et que vous ne souhaitez empêcher le comportement par défaut que pour un petit nombre d'entre elles, cette directive n'est pas très efficace. Cependant, je le veux presque toujours preventDefault, donc j'utilise cette directive partout dans mes applications AngularJS.


2
Merci @matejkramny, critique très constructive. Des suggestions pour le rendre moins "salissant"?
tennisgent

1
Comme indiqué dans d'autres réponses, avoir un hrefattribut vide a un comportement variable dans différents navigateurs. Bien que je convienne que c'est de loin la solution la plus propre et la plus efficace, elle a quelques problèmes entre les navigateurs. L'utilisation de l'approche directive permet au navigateur de gérer la <a>balise comme il le ferait normalement, tout en obtenant à l'OP la réponse qu'il recherchait.
tennisgent

2
Cela fonctionne et tout sauf son exagération sérieuse pour un problème très simple. Angular vous donne accès à l'objet événement avec $ event, vous pouvez donc faire exactement ce que vous feriez dans des événements js simples ou jquery click. Voir cette réponse stackoverflow.com/a/19240232/1826354 et mon commentaire à ce sujet
Charlie Martin

1
Oui. Vous pouvez. Il y a deux autres réponses sur ce post qui font déjà ce que vous décrivez. Et je suis d'accord, c'est exagéré. C'est pourquoi j'ai fait la mise à jour que j'ai faite.
tennisgent

6
C'est très bien si vous ne vous souciez pas de l' accessibilité sur votre site. En laissant le href, il n'est pas possible d'utiliser votre clavier pour taper sur cet élément. Sauf si vous avez ajouté un tabindex. Avec tout cela à l'esprit, pourquoi ne pas simplement utiliser preventDefault ...? C'est pour ça qu'il est là.
duhseekoh

319

Selon la documentation de ngHref, vous devriez pouvoir laisser le href ou faire 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 />

6
C'est la seule vraie bonne réponse. Tout ce qui nécessite de toucher le DOM ou les événements natifs est discutable
vincent

4
Ceci est la bonne réponse. Si vous supprimez href de l'attribut <a>, AngularJS appellera prévention par défaut:
pkozlowski.opensource

25
Le seul inconvénient de la suppression de l'attribut href est que vous perdez le curseur «pointeur» normal lorsque vous survolez un lien. Vous pouvez résoudre ce problème en ajoutant une règle à votre feuille de style: a: hover {cursor: pointer; }
karlgold

35
Gardez à l'esprit que cela rend également la balise non tabable, ce qui n'est pas très convivial.
Richard Szalay

3
Pour moi, le navigateur bouillonne toujours l'action de clic: /
Matej

238

Vous pouvez passer l'objet $ event à votre méthode et l'appeler $event.preventDefault()afin que le traitement par défaut ne se produise pas:

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

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

13
oui, et vous pouvez également appeler $ event.stopPropagation () pour que l'événement ne se propage pas (en gros, vous avez accès à l'objet Event tel que défini par le W3C: w3.org/TR/DOM-Level-2- Events / events.html # Events-Event )
mna

1
Veuillez noter qu'au moment où cela a été écrit, laisser le href vide (ou absent) ne fonctionnait pas sur IE8 / 9 et Opera. C'était il y a un an, et je n'ai pas eu la chance de l'essayer à nouveau (j'ai ouvert un problème à l'époque sur Github, mais je pense qu'ils ont réinitialisé les problèmes il y a quelque temps). Si cela est corrigé (ou si vous ne vous souciez pas de ces navigateurs), alors certainement, utilisez la réponse de Chris! Si quelqu'un peut l'essayer et commenter / modifier la réponse, c'est encore mieux.
mna

1
@PuerkitoBio - Je peux confirmer que IE8 et ci-dessous nécessitent toujours $event.preventDefault()... la taxe IE.
Scotty.NET

4
transmettre l'événement $ au contrôleur signifie référencer le DOM dans votre contrôleur, ce qui signifie que vous avez couplé votre vue et votre contrôleur. Vous pouvez appeler les méthodes $ event directement dans votre expression évaluée: ng-click="do(); $event.preventDefault()par exemple ... bien que ce ne soit pas nécessaire car la réponse de @ Chris ci-dessous est la bonne. Cela pourrait aider les gens dans des situations où ils ont imbriqué ngClicks et qu'ils souhaitent appeler $event.stopPropagation(), cependant.
Ben Lesh

2
Enfin une solution conviviale pour le référencement. Vous pouvez également mettre à jour l'URL du navigateur sans recharger la vue ng - joelsaupe.com/programming/… De cette façon, vous avez des liens qui ne rechargent pas votre vue, mais peuvent être ouverts dans un nouvel onglet et les moteurs de recherche peuvent les suivre. YAY!
Tom

111

Je préfère utiliser des directives pour ce genre de chose. Voici un exemple

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

Et le code de directive pour eat-click:

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

Vous pouvez maintenant ajouter l' eat-clickattribut à n'importe quel élément et il sera preventDefault()automatiquement édité.

Avantages:

  1. Vous n'avez pas besoin de passer l' $eventobjet laid dans votre do()fonction.
  2. Votre contrôleur est plus testable à l'unité car il n'a pas besoin de dérober l' $eventobjet

1
@Kato Angular est livré avec son propre sous-ensemble de jQuery, pour nous faciliter la vie.
Neil

3
Avantage supplémentaire - cela fonctionne également dans IE8 et ci-dessous, si cela vous intéresse.
Scotty.NET

1
Je reçois Error: $ is not defined. Qu'est-ce que je rate?
Bilal Mirza

7
J'ai essayé angular.element(element).bind('click', function(event){...});. Ça a marché. Ref: jQLite
Bilal Mirza

3
Introduire une directive pour quelque chose de simple comme ça semble un peu gonflé si vous me demandez ... Vérifiez Chris sa réponse ci
Wilt

54

Bien que Renaud ait donné une excellente solution

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

J'ai personnellement trouvé que vous avez également besoin de $ event.stopPropagation () dans certains cas pour éviter certains des effets secondaires

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

sera ma solution


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

13
C'est la meilleure solution. Bien sûr, vous pouvez également passer l'objet $ event à votre fonction scope. Comme ng-click = "myFunction ($ event, otherParams) puis $ event.preventDefault () dans myFunction. Rouler votre propre directive pour cela est exagéré
Charlie Martin

34

La solution la plus simple que j'ai trouvée est celle-ci:

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

Ce n'est pas seulement le plus simple, mais il ne couple pas non plus les événements avec le contrôleur comme suggéré dans d'autres solutions. Le couplage d'événements à un contrôleur est quelque chose que vous devez éviter à tout prix car cela rend les tests beaucoup plus difficiles.
jornare

11

Donc, en lisant ces réponses, @Chris a toujours la réponse la plus "correcte", je suppose, mais il a un problème, il n'affiche pas le "pointeur" ....

Voici donc deux façons de résoudre ce problème sans avoir besoin d'ajouter un curseur: style de pointeur:

  1. Utilisez javascript:void(0)au lieu de #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. À utiliser $event.preventDefault()dans la ng-clickdirective (pour ne pas joncher votre contrôleur avec des références DOM):

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

Personnellement, je préfère le premier au second. javascript:void(0)a d'autres avantages qui sont discutés ici . Il y a également une discussion sur le «JavaScript discret» dans ce lien qui est effroyablement récent et ne s'applique pas nécessairement directement à une application angulaire.


2
Pourquoi cela est-il rejeté? J'ai également remarqué que le pointeur ne s'affiche pas avec la réponse de Chris.
Wim Deblauwe

2
javascript: void (0) est bien meilleur que # qui peut agir sur l'itinéraire si vous le configurez mal. +1
Shay Elkayam

2
J'étais sur le point d'écrire une réponse à cela. Vous pouvez également fairejavascript:;
Adrian

10

Vous pouvez faire comme suit

1.Retirer l' hrefattribut de la abalise anchor ( )

2.Réglez le curseur du pointeur en CSS sur les éléments de clic

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

9

HTML

ici pure angularjs: près de la fonction ng-click, vous pouvez écrire la fonction preventDefault () en séparant le point-virgule

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

JS

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

(ou)

vous pouvez procéder de cette façon, cela est déjà discuté ici.

HTML

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

JS

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

7

Essayez cette option qui je vois n'est pas encore listée ci-dessus:

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

6

Puisque vous créez une application Web, pourquoi avez-vous besoin de liens?

Échangez vos ancres contre des boutons!

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

2
Étant donné que wen ne peut pas avoir des ancres webapps?
Robin van Baalen

1
Je faisais allusion au fait que la plupart des applications Web n'ont pas besoin de liens relatifs comme les sites Web, elles utilisent aussi souvent des liens d'ancrage comme déclencheurs javascript et ne font pas de lien vers une page; par conséquent, un bouton avec la réinitialisation du style par défaut est tout aussi correct sémantiquement, sinon plus.
sidonaldson

3
@sidonaldson En fait, beaucoup d'applications Web dépendent aujourd'hui fortement des liens. Regardez le routage angularjs.
Robert

1
@Robert oui, mais si vous utilisez le routage intégré, vous n'avez pas à gérer la prévention des défauts, c'est fait pour vous. Je suppose que puisque l'affiche veut annuler un lien d'ancrage, il parle de liens non routés. Par exemple, lancement d'un modal, etc.
sidonaldson

2
Cette réponse devrait avoir beaucoup plus de votes positifs. Dans de nombreux cas, si vous souhaitez empêcher le comportement d'ancrage par défaut, vous utilisez l'ancre comme bouton et non comme lien.
ItsCosmo

6

si toujours d'actualité:

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

...

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

...

6

Le moyen le plus sûr d'éviter les événements sur un href serait de le définir comme

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

5

J'irais avec:

<a ng-click="do()">Click</a>
  • car selon les documents, vous devriez pouvoir quitter le href et Angular gérera alors le défaut par défaut pour vous!

L'ensemble de cette chose empêchant le défaut m'a dérouté, j'ai donc créé un JSFiddle pour illustrer quand et où Angular empêche le défaut .

Le JSFiddle utilise la directive a d'Angular - il devrait donc être EXACTEMENT le même. Vous pouvez voir le code source ici: un code source de balise

J'espère que cela aidera la clarification pour certains.

J'aurais aimé poster le doc sur ngHref mais je ne peux pas à cause de ma réputation.


5

C'est ce que je fais toujours. Fonctionne comme un charme!

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

4

Ou si vous avez besoin en ligne, vous pouvez le faire:

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

4

Beaucoup de réponses semblent exagérées. POUR MOI, ça marche

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

2

J'ai besoin d'une présence de la valeur de l'attribut href pour la dégradation (lorsque js est désactivé), donc je ne peux pas utiliser l'attribut href vide (ou "#"), mais le code ci-dessus n'a pas fonctionné pour moi, car j'ai besoin d'un événement ( e) variable. J'ai créé ma propre directive:

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

Empruntant à la réponse de tennisgent. J'aime que vous n'ayez pas à créer une directive personnalisée à ajouter sur tous les liens. Cependant, je n'ai pas pu faire fonctionner son IE8. Voici ce qui a finalement fonctionné pour moi (en utilisant angulaire 1.0.6).

Notez que 'bind' vous permet d'utiliser jqLite fourni par angular donc pas besoin de boucler avec jQuery complet. Également requis la méthode 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
Cela tue trop. Par exemple, lorsque j'utilise Twitter Bootstrap dropdown, je veux la propagation mais pas le comportement par défaut. Cet extrait n'est PAS recommandé.
Robin van Baalen

2

J'ai rencontré ce même problème lors de l'utilisation d'ancres pour une liste déroulante de bootstrap angulaire. La seule solution que j'ai trouvée qui a évité les effets secondaires indésirables (c'est-à-dire la liste déroulante ne se fermant pas en raison de l'utilisation de preventDefault ()) était d'utiliser ce qui suit:

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

2

si vous ne voulez jamais aller à href ... vous devez changer votre balisage et utiliser un bouton et non une balise d'ancrage car sémantiquement ce n'est pas une ancre ou un lien. Inversement, si vous avez un bouton qui vérifie puis redirige, ce devrait être un lien / une balise d'ancrage et non un bouton ... à des fins de référencement ainsi que de test [insérer la suite de tests ici].

pour les liens que vous souhaitez uniquement rediriger de manière conditionnelle comme l'invite à enregistrer les modifications ou à reconnaître l'état sale ... vous devez utiliser ng-click = "someFunction ($ event)" et dans someFunction sur votre validationError preventDefault et ou stopPropagation.

aussi parce que vous pouvez ne signifie pas que vous devriez . javascript: void (0) a bien fonctionné à l'époque ... mais à cause des hackers / crackers néfastes, les navigateurs signalent les applications / sites qui utilisent javascript dans un href ... ne voyez aucune raison de ducktype ci-dessus ..

c'est 2016 depuis 2010 ne devrait être aucune raison d'utiliser des liens qui agissent comme des boutons ... si vous devez toujours prendre en charge IE8 et ci-dessous, vous devez réévaluer votre établissement.


1
/* NG CLICK PREVENT DEFAULT */

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

1

Ce que vous devez faire, c'est supprimer hrefcomplètement l' attribut.

Si vous regardez le code source de la adirective élément (qui fait partie du noyau angulaire), il indique à la ligne 29 - 31:

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

Ce qui signifie qu'Angular résout déjà le problème des liens sans href. Le seul problème que vous avez toujours est le problème css. Vous pouvez toujours appliquer le style de pointeur aux ancres avec ng-clics, par exemple:

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

Ainsi, vous pouvez même rendre votre site plus accessible en marquant de vrais liens avec un style subtil et différent, puis des liens qui remplissent des fonctions.

Bon codage!


1

Vous pouvez désactiver la redirection d'URL dans le Html5Mode de $ location pour atteindre l'objectif. Vous pouvez le définir à l'intérieur du contrôleur spécifique utilisé pour la page. Quelque chose comme ça:

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


1

Si vous utilisez Angular 8 ou plus, vous pouvez créer une directive:

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

}

Sur votre balise d'ancrage sur le composant, vous pouvez le câbler comme ceci:

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

Le module d'application doit avoir sa déclaration:

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


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective

-1

Une alternative pourrait être:

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

1
C'est une pratique horrible d'abuser d'un spanpour cliquer. Allez juste pour la réponse de @ tennisgent - ancres sans hrefdéfini.
Robin van Baalen


1
Si vous faites <span> Bonjour </span> depuis quand est-ce cliquable? Ce que la spécification dit probablement, c'est qu'un <span> peut être utilisé à l'intérieur d'un élément cliquable tel que <a> ou <button>. Mais là encore, tous les éléments de bloc en ligne (img, span, etc.) peuvent être rendus cliquables de cette façon. Un span lui-même n'est pas cliquable.
Robin van Baalen

1
@RobinvanBaalen Veuillez consulter les liens ci-dessus. L'attribut onclick pour les éléments span est défini depuis au moins HTML 4.01.
Terence Bandoian du

1
Je n'ai jamais dit que l'ajout d'un gestionnaire d'événements ne fonctionnerait pas pendant une durée. J'ai dit que c'était une mauvaise pratique de rendre un élément non cliquable comme un span, div, section, ul, li, etc. cliquable en utilisant un gestionnaire d'événement javascript. Tout est question de sémantique. La sémantique au sein du HTML est la pratique consistant à donner du contenu et une structure à la page en utilisant l'élément approprié.
Robin van Baalen
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.