Come prevenire default sui tag anchor?


342

Diciamo che ho un tag anchor come

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

Come posso impedire al browser di navigare su # in AngularJS ?


14
Come Chris dice sotto, lascia fuori il href.
Jesse,

13
Non è quello che dice Chris, Jesse, href=''per mantenere il comportamento del puntatore del mouse.
Oma,

1
Basta rimuovere il segno # da href, è OK.
HENG Vongkol,

4
O semplicemente usa href = "javascript: void (0);" per mantenere il puntatore ma non agire (fino a quando non aggiungi un altro gestore di clic)
Robba,

1
Potresti cambiare la risposta accettata alla migliore e la più votata da Chris - stackoverflow.com/a/11672909 ?
Piotr Dobrogost,

Risposte:


259

AGGIORNAMENTO : Da allora ho cambiato idea su questa soluzione. Dopo più sviluppo e tempo impiegato a lavorare su questo, credo che una soluzione migliore a questo problema sia quella di fare quanto segue:

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

E quindi aggiorna il tuo cssper avere una regola extra:

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

È molto più semplice e offre esattamente la stessa funzionalità ed è molto più efficiente. Spero che possa essere utile a chiunque cerchi questa soluzione in futuro.


Quella che segue è la mia soluzione precedente, che lascio qui solo per scopi legacy:

Se riscontri molto questo problema, una semplice direttiva che risolve questo problema è la seguente:

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

Controlla tutti i tag anchor ( <a></a>) per vedere se il loro hrefattributo è una stringa vuota ( "") o un hash ( '#') o se esiste un ng-clickcompito. Se rileva una di queste condizioni, rileva l'evento e impedisce il comportamento predefinito.

L'unico lato negativo è che esegue questa direttiva per tutti i tag di ancoraggio. Pertanto, se nella pagina sono presenti molti tag di ancoraggio e si desidera solo impedire il comportamento predefinito per un numero limitato di essi, questa direttiva non è molto efficace. Tuttavia, quasi sempre lo voglio preventDefault, quindi uso questa direttiva ovunque nelle mie app AngularJS.


2
Grazie @matejkramny, critiche molto costruttive. Qualche suggerimento per renderlo meno "disordinato"?
tennisgent

1
Come indicato in altre risposte, avere un hrefattributo vuoto ha un comportamento variabile nei diversi browser. Sebbene sia d'accordo che sia di gran lunga la soluzione più pulita ed efficiente, presenta alcuni problemi tra browser. L'uso dell'approccio direttiva consente al browser di gestire il <a>tag come farebbe normalmente, ma ottenendo comunque l'OP la risposta che stavano cercando.
tennisgent

2
Questo funziona e tutto tranne che sul serio è eccessivo per un problema molto semplice. Angular ti dà accesso all'oggetto evento con $ event, così puoi fare esattamente quello che faresti in eventi jsery o jquery con clic. Vedi questa risposta stackoverflow.com/a/19240232/1826354 e il mio commento al riguardo
Charlie Martin

1
Sì. Puoi. Ci sono altre due risposte su questo post che fanno già quello che stai descrivendo. E sono d'accordo, è eccessivo. Ecco perché ho fatto l'aggiornamento che ho fatto.
tennisgent,

6
Questo va bene se non ti interessa l' accessibilità sul tuo sito. Tralasciando l'href, non è possibile usare la tastiera per accedere a quell'elemento. A meno che tu non abbia aggiunto un tabindex. Tenendo presente tutto ciò, perché non usare solo preventDefault ...? Ecco a cosa serve.
duhseekoh,

319

Secondo i documenti per ngHref dovresti essere in grado di lasciare fuori href o fare 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
È l'unica vera buona risposta. Tutto ciò che richiede di toccare il DOM o gli eventi nativi è discutibile
vincent

4
Questa è la risposta corretta Se
elimini

25
L'unico aspetto negativo di lasciare l'attributo href è che si perde il normale cursore "puntatore" quando si passa sopra un collegamento. Puoi risolvere questo problema aggiungendo una regola al tuo foglio di stile: a: hover {pointer: pointer; }
karlgold,

35
Tieni presente che ciò rende il tag non tabulabile, il che non è molto accessibile.
Richard Szalay,

3
Per me, il browser crea ancora il clic: /
Matej,

238

È possibile passare l'oggetto $ event al proprio metodo e richiamarlo $event.preventDefault(), in modo che l'elaborazione predefinita non si verifichi:

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

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

13
sì, e puoi anche chiamare $ event.stopPropagation () in modo che l'evento non si riempia di bolle (in pratica, hai accesso all'oggetto Event come definito dal W3C: w3.org/TR/DOM-Level-2- Events / events.html # Events-Event )
mna

1
Si noti che al momento della stesura di questo documento, lasciare l'href vuoto (o assente) non funzionava su IE8 / 9 e Opera. È stato un anno fa e non ho avuto la possibilità di riprovare (ho aperto un problema allora su Github, ma penso che abbiano risolto i problemi qualche tempo fa). Se questo è risolto (o non ti interessa per questi browser), allora usa la risposta di Chris! Se qualcuno può provarlo e commentare / modificare la risposta, ancora meglio.
mna,

1
@PuerkitoBio - Posso confermare che IE8 e inferiori richiedono ancora $event.preventDefault()... IE tax.
Scotty.NET,

4
passare l'evento $ al controller significa fare riferimento al DOM nel controller, il che significa che hai accoppiato la tua vista e il tuo controller. Puoi chiamare i metodi $ event direttamente nell'espressione valutata: ng-click="do(); $event.preventDefault()ad esempio ... anche se non è necessario perché la risposta di @ Chris di seguito è quella corretta. Ciò potrebbe aiutare le persone in situazioni in cui hanno nidificato ngClicks e che vogliono chiamare $event.stopPropagation(), tuttavia.
Ben Lesh,

2
Finalmente una soluzione SEO friendly. Potresti voler anche aggiornare l'URL del browser senza ricaricare ng-view - joelsaupe.com/programming/… In questo modo hai collegamenti che non ricaricano la tua vista, ma possono essere aperti in una nuova scheda e i motori di ricerca possono seguirli. SÌÌ!
Tom,

111

Preferisco usare le direttive per questo tipo di cose. Ecco un esempio

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

E il codice direttiva per eat-click:

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

Ora puoi aggiungere l' eat-clickattributo a qualsiasi elemento e verrà preventDefault()"ed automagicamente".

Benefici:

  1. Non devi passare l' $eventoggetto brutto nella tua do()funzione.
  2. Il controller è più unità testabile perché non ha bisogno di stub $eventoggetto

1
@Kato Angular viene fornito con un proprio sottoinsieme di jQuery, per semplificarci la vita.
Neil,

3
Vantaggio aggiuntivo : funziona anche in IE8 e versioni successive, se questo è importante per te.
Scotty.NET,

1
Sto ottenendo Error: $ is not defined. Cosa mi sto perdendo?
Bilal Mirza,

7
Ci ho provato angular.element(element).bind('click', function(event){...});. Ha funzionato. Rif: jQLite
Bilal Mirza,

3
Introdurre una direttiva per qualcosa di semplice come quello sembra un po 'gonfio se mi chiedi ... Controlla Chris la sua risposta qui sotto
Wilt

54

Anche se Renaud ha dato un'ottima soluzione

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

Personalmente ho scoperto che è necessario anche $ event.stopPropagation () in alcuni casi per evitare alcuni degli effetti collaterali

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

sarà la mia soluzione


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

13
Questa è la soluzione migliore Ovviamente, potresti anche passare l'oggetto $ event alla tua funzione scope. Come ng-click = "myFunction ($ event, otherParams) e quindi $ event.preventDefault () in myFunction. Far rotolare la propria direttiva per questo è eccessivo
Charlie Martin

34

La soluzione più semplice che ho trovato è questa:

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

Questo non è solo il più semplice, ma non accoppia eventi con il controller come suggerito in altre soluzioni. L'accoppiamento di eventi a un controller è qualcosa che dovresti evitare a tutti i costi poiché rende i test molto più difficili.
jornare,

11

Quindi, leggendo queste risposte, @Chris ha ancora la risposta più "corretta", suppongo, ma ha un problema, non mostra il "puntatore" ....

Quindi qui ci sono due modi per risolvere questo problema senza bisogno di aggiungere un cursore: stile puntatore:

  1. Usa javascript:void(0)invece di #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. Utilizzare $event.preventDefault()nella ng-clickdirettiva (quindi non eseguire il junk del controller con riferimenti relativi al DOM):

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

Personalmente preferisco il primo al secondo. javascript:void(0)ha altri vantaggi che sono discussi qui . Si discute anche di "JavaScript discreto" in quel link che è spaventosamente recente e non si applica necessariamente direttamente a un'applicazione angolare.


2
Perché questo è downvoted? Ho anche notato che il puntatore non viene visualizzato con la risposta di Chris.
Wim Deblauwe,

2
javascript: void (0) è molto meglio di # che può agire sulla rotta se lo si configura in modo errato. +1
Shay Elkayam,

2
Stavo per scrivere una risposta a questo. Puoi anche farejavascript:;
Adrian il

10

Puoi fare come segue

1.Rimuovere l' hrefattributo dal atag anchor ( )

2.Impostare il cursore del puntatore in CSS per fare clic sugli elementi clic

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

9

HTML

qui pure angularjs: vicino alla funzione ng-click puoi scrivere la funzione preventDefault () separando il punto e virgola

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

JS

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

(o)

puoi procedere in questo modo, ne abbiamo già discusso qui.

HTML

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

JS

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

7

Prova questa opzione che posso vedere non è ancora elencata sopra:

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

6

Dal momento che stai creando un'app Web, perché hai bisogno di link?

Scambia le tue ancore in bottoni!

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

2
Da quando le webapp non possono avere ancore?
Robin van Baalen,

1
Mi riferivo al punto che la maggior parte delle app web non ha bisogno di collegamenti relativi come fanno i siti Web, spesso usano anche collegamenti di ancoraggio come trigger javascript e non si collegano a una pagina; pertanto un pulsante con il ripristino dello stile predefinito è altrettanto semanticamente corretto se non di più.
Sidonaldson,

3
@sidonaldson In realtà, molte app web al giorno d'oggi dipendono fortemente dai collegamenti. Guarda il routing angularjs.
Robert,

1
@Robert sì, ma se usi il routing integrato non devi gestire la prevenzione predefinita, è fatto per te. Presumo poiché il poster vuole annullare un collegamento di ancoraggio, sta parlando di collegamenti non di rotta. Ad esempio il lancio di un modale ecc.
Sidonaldson,

2
Questa risposta dovrebbe avere molti più voti. In molti casi, se si desidera impedire il comportamento predefinito dell'ancora, si sta utilizzando l'ancora come pulsante e non come collegamento.
ItsCosmo,

6

se ancora pertinente:

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

...

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

...

6

Il modo più sicuro per evitare eventi su un href sarebbe definirlo come

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

5

Vorrei andare con:

<a ng-click="do()">Click</a>
  • perché secondo i documenti dovresti essere in grado di lasciare l'href e quindi Angular gestirà la prevenzione predefinita per te!

Tutto questo per prevenire la cosa predefinita mi ha confuso, quindi ho creato un JSFiddle che illustra quando e dove Angular impedisce la default .

JSFiddle sta usando Angular come una direttiva, quindi dovrebbe ESATTAMENTE essere lo stesso. Puoi vedere il codice sorgente qui: un codice sorgente tag

Spero che questo possa aiutare a chiarire alcuni.

Mi sarebbe piaciuto pubblicare il documento su ngHref ma non posso a causa della mia reputazione.


5

Questo è quello che faccio sempre. Funziona come un fascino!

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

4

O se hai bisogno in linea, puoi farlo:

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

4

Molte risposte sembrano eccessive. PER ME, funziona

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

2

Ho bisogno di una presenza del valore dell'attributo href per il degrado (quando js è spento), quindi non posso usare l'attributo href vuoto (o "#"), ma il codice sopra non ha funzionato per me, perché ho bisogno di un evento ( e) variabile. Ho creato la mia direttiva:

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

In HTML:

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

2

Prendendo in prestito dalla risposta di tennisgent. Mi piace che non sia necessario creare una direttiva personalizzata da aggiungere a tutti i collegamenti. Tuttavia, non sono riuscito a farlo funzionare in IE8. Ecco cosa finalmente ha funzionato per me (usando l'angolare 1.0.6).

Si noti che 'bind' consente di utilizzare jqLite fornito da angular, quindi non è necessario eseguire il wrapping con jQuery completo. Richiesto anche il metodo 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
Questo uccide molto. Ad esempio, quando uso Twitter Bootstrap dropdown, voglio la propagazione ma non il comportamento predefinito. Questo frammento NON è raccomandato.
Robin van Baalen,

2

Ho riscontrato questo stesso problema durante l'utilizzo delle ancore per un menu a discesa angolare del bootstrap. L'unica soluzione che ho scoperto che ha evitato effetti collaterali indesiderati (ad es. Il menu a discesa non chiuso a causa dell'utilizzo di preventDefault ()) è stato quello di utilizzare quanto segue:

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

2

se non vuoi mai andare su href ... dovresti cambiare il tuo markup e usare un pulsante non un tag anchor perché semanticamente non è un anchor o un link. Inversamente se si dispone di un pulsante che esegue alcuni controlli e quindi reindirizza dovrebbe essere un tag link / anchor e non un pulsante ... per scopi SEO e test [inserire la suite di test qui].

per i collegamenti che desideri reindirizzare solo in modo condizionale come la richiesta di salvare le modifiche o riconoscere lo stato sporco ... dovresti usare ng-click = "someFunction ($ event)" e in someFunction sulla tua convalidaError preventDefault e o stopPropagation.

anche solo perché puoi non significa che dovresti . javascript: void (0) ha funzionato bene nel passato ... ma a causa dei malvagi hacker / cracker i browser contrassegnano app / siti che usano javascript in un href ... non vedo alcun motivo per ducktype sopra ..

è il 2016 dal 2010 non dovrebbe essere un motivo per utilizzare i collegamenti che agiscono come pulsanti ... se devi ancora supportare IE8 e al di sotto devi rivalutare la tua sede di attività.


1
/* NG CLICK PREVENT DEFAULT */

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

1

Quello che dovresti fare è omettere completamente l' hrefattributo.

Se guardi il codice sorgente per la adirettiva element (che fa parte del nucleo angolare), indica alla riga 29 - 31:

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

Il che significa che Angular sta già risolvendo il problema dei collegamenti senza un href. L'unico problema che hai ancora è il problema CSS. Puoi ancora applicare lo stile del puntatore alle ancore che hanno ng-clic, ad esempio:

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

Quindi, potresti persino rendere il tuo sito più accessibile contrassegnando collegamenti reali con uno stile sottile e diverso rispetto ai collegamenti che svolgono funzioni.

Buona programmazione!


1

È possibile disabilitare il reindirizzamento dell'URL all'interno della modalità Html5 di $ location per raggiungere l'obiettivo. È possibile definirlo all'interno del controller specifico utilizzato per la pagina. Qualcosa come questo:

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


1

Se si utilizza Angular 8 o più, è possibile creare una direttiva:

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

}

Sul tuo tag di ancoraggio sul componente puoi collegarlo in questo modo:

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

Il modulo app dovrebbe avere la sua dichiarazione:

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


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective

-1

Un'alternativa potrebbe essere:

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

1
È una pratica orribile abusare di a spanscopo di clic. Prova la risposta di @ tennisgent: ancore senza hrefdefinizione.
Robin van Baalen,


1
Se fai <span> Ciao </span> da quando è possibile fare clic? Quello che probabilmente dice la specifica è che un <span> può essere usato all'interno di un elemento cliccabile come <a> o <button>. Ma ancora una volta, tutti gli elementi di blocco in linea (img, span, ecc.) Possono essere resi cliccabili in questo modo. Una campata in sé non è selezionabile.
Robin van Baalen il

1
@RobinvanBaalen Si prega di consultare i link sopra. L'attributo onclick per gli elementi span è stato definito almeno da HTML 4.01.
Terence Bandoian,

1
Non ho mai detto che l'aggiunta di un gestore eventi non avrebbe funzionato per un arco di tempo. Ho detto che è una cattiva pratica rendere un elemento non cliccabile come span, div, section, ul, li, ecc. Cliccabile usando un gestore di eventi javascript. Si tratta di semantica. La semantica all'interno dell'HTML è la pratica di fornire contenuti e significato alla struttura della pagina utilizzando l'elemento appropriato.
Robin van Baalen il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.