Jak zapobiegać domyślnym znacznikom zakotwiczenia?

342

Powiedzmy, że mam tag zakotwiczenia, taki jak

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

Jak mogę uniemożliwić przeglądarce przejście do # w AngularJS ?

Miki
źródło
14
Jak mówi Chris poniżej, po prostu pomiń href.
Jesse
13
Nie tak mówi Chris, Jesse, href=''aby zachować zachowanie wskaźnika myszy.
oma
1
Po prostu usuwasz znak # z href, jest OK.
HENG Vongkol
4
Lub po prostu użyj href = "javascript: void (0);" zachować wskaźnik, ale nie wykonywać żadnych działań (dopóki nie dodasz innej procedury obsługi kliknięć)
Robba,
1
Czy możesz zmienić zaakceptowaną odpowiedź na najlepszą i najczęściej głosowaną przez Chrisa - stackoverflow.com/a/11672909 ?
Piotr Dobrogost

Odpowiedzi:

259

AKTUALIZACJA : Od tego czasu zmieniłem zdanie na temat tego rozwiązania. Po dalszym rozwoju i czasie poświęconym na pracę nad tym rozwiązaniem, uważam, że lepszym rozwiązaniem tego problemu jest wykonanie następujących czynności:

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

A następnie zaktualizuj, cssaby mieć dodatkową regułę:

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

Jest o wiele prostszy i zapewnia dokładnie taką samą funkcjonalność oraz jest znacznie bardziej wydajny. Mam nadzieję, że może to być pomocne dla każdego, kto szuka tego rozwiązania w przyszłości.


Oto moje poprzednie rozwiązanie, które zostawiam tutaj tylko w celach starszych:

Jeśli często masz ten problem, prosta dyrektywa, która rozwiązałaby ten problem, jest następująca:

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

Sprawdza wszystkie tagi anchor ( <a></a>), aby sprawdzić, czy ich hrefatrybut jest pustym ciągiem ( "") lub hashem ( '#'), czy istnieje ng-clickprzypisanie. Jeśli znajdzie którykolwiek z tych warunków, łapie zdarzenie i zapobiega domyślnemu zachowaniu.

Jedynym minusem jest to, że uruchamia tę dyrektywę dla wszystkich tagów zakotwiczenia. Jeśli więc masz na stronie wiele tagów zakotwiczenia i chcesz zapobiec domyślnemu zachowaniu tylko niewielkiej ich liczby, to ta dyrektywa nie jest zbyt wydajna. Jednak prawie zawsze chcę preventDefault, więc używam tej dyrektywy w moich aplikacjach AngularJS.

tenisista
źródło
2
Dzięki @matejkramny, bardzo konstruktywna krytyka. Jakieś sugestie, aby uczynić go mniej „niechlujnym”?
Tennisgent
1
Jak stwierdzono w innych odpowiedziach, posiadanie pustego hrefatrybutu ma różne zachowanie w różnych przeglądarkach. Chociaż zgadzam się, że jest to zdecydowanie najczystsze i najbardziej wydajne rozwiązanie, ma pewne problemy z przeglądarkami. Użycie metody dyrektywy pozwala przeglądarce na obsługę <a>tagu w normalny sposób, ale nadal uzyskuje OP żądaną odpowiedź.
Tennisgent
2
To działa i to wszystko z wyjątkiem poważnego przesady w przypadku bardzo prostego problemu. Angular daje ci dostęp do obiektu zdarzenia za pomocą $ event, dzięki czemu możesz robić dokładnie to, co robisz w zwykłych zdarzeniach js lub jquery click. Zobacz tę odpowiedź stackoverflow.com/a/19240232/1826354 i mój komentarz na ten temat
Charlie Martin
1
Tak. Możesz. W tym poście są dwie inne odpowiedzi, które już robią to, co opisujesz. I zgadzam się, to przesada. Dlatego dokonałem aktualizacji, którą zrobiłem.
tenisista
6
Jest to w porządku, jeśli nie zależy Ci na dostępności w swojej witrynie. Pomijając href, nie można użyć klawiatury, aby tabulować ten element. Chyba że dodałeś tabindex. Mając to wszystko na uwadze, dlaczego nie skorzystać z funkcji preventDefault ...? Po to jest.
duhseekoh
319

Według dokumentacji dla ngHref powinieneś być w stanie zrezygnować z href lub zrobić 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 />
Chris
źródło
6
To jedyna dobra odpowiedź. Wszystko, co wymaga dotknięcia DOM lub wydarzeń rodzimych, jest wątpliwe
Vincent
4
To jest poprawna odpowiedź. Jeśli upuścisz href z atrybutu <a>, AngularJS zadzwoni, aby zapobiec domyślnemu:
pkozlowski.opensource 21.04.2013
25
Jedynym minusem pominięcia atrybutu href jest to, że podczas najechania kursorem na link tracisz normalny kursor „wskaźnikowy”. Możesz to naprawić, dodając regułę do arkusza stylów: a: hover {kursor: wskaźnik; }
karlgold,
35
Należy pamiętać, że powoduje to, że tag nie może być tabulowany, co nie jest bardzo przyjazne dla osób niepełnosprawnych.
Richard Szalay,
3
Dla mnie przeglądarka nadal powoduje kliknięcie akcji: /
Matej
238

Możesz przekazać obiekt zdarzenia $ do swojej metody i wywołać $event.preventDefault()go, aby domyślne przetwarzanie nie nastąpiło:

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

// then in your controller.do($event) method
$event.preventDefault()
mna
źródło
13
tak, możesz także wywołać $ event.stopPropagation (), aby zdarzenie się nie rozebrało (w zasadzie masz dostęp do obiektu Event zdefiniowanego przez W3C: w3.org/TR/DOM-Level-2- Events / events.html # Events-Event )
12:12
1
Zwróć uwagę, że w chwili pisania tego tekstu pozostawienie pustego obszaru (lub jego brak) nie działało w IE8 / 9 i Operze. To było rok temu i nie miałem okazji tego spróbować ponownie (wtedy otworzyłem problem na Githubie, ale myślę, że jakiś czas temu zresetowali problemy). Jeśli zostanie to naprawione (lub nie zależy ci na tych przeglądarkach), skorzystaj z odpowiedzi Chrisa! Jeśli ktoś może to wypróbować i skomentować / edytować odpowiedź, jeszcze lepiej.
mna
1
@PuerkitoBio - Mogę potwierdzić, że IE8 i poniżej nadal wymagają $event.preventDefault()... podatku IE.
Scotty.NET,
4
przekazanie zdarzenia $ do kontrolera oznacza odwołanie się do modelu DOM w kontrolerze, co oznacza, że ​​połączyłeś swój widok z kontrolerem. Możesz wywołać metody zdarzenia $ bezpośrednio w sprawdzonym wyrażeniu: ng-click="do(); $event.preventDefault()na przykład ... chociaż nie jest to konieczne, ponieważ odpowiedź @ Chrisa poniżej jest poprawna. Może to pomóc ludziom w sytuacjach, w których zagnieżdżono ngClicks i chcą jednak zadzwonić $event.stopPropagation().
Ben Lesh,
2
Wreszcie rozwiązanie przyjazne SEO. Możesz także zaktualizować adres URL przeglądarki bez ponownego ładowania ng-view - joelsaupe.com/programming/... W ten sposób masz linki, które nie ładują twojego widoku, ale można je otworzyć w nowej karcie i wyszukiwarki mogą je śledzić. TAK!
Tom
111

Wolę używać do tego celu dyrektyw . Oto przykład

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

Kod dyrektywy dla eat-click:

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

Teraz możesz dodać eat-clickatrybut do dowolnego elementu, a zostanie on preventDefault()zautomatyzowany.

Korzyści:

  1. Nie musisz przekazywać brzydkiego $eventobiektu do swojej do()funkcji.
  2. Twój kontroler jest bardziej testowalny jednostkowo, ponieważ nie musi wycierać $eventobiektu
djsmith
źródło
1
@Kato Angular ma własny podzbiór jQuery, aby ułatwić nam życie.
Neil
3
Dodatkowa korzyść - działa to również w IE8 i poniżej, jeśli ma to dla Ciebie znaczenie.
Scotty.NET,
1
Dostaję Error: $ is not defined. czego mi brakuje?
Bilal Mirza
7
Próbowałem angular.element(element).bind('click', function(event){...});. Zadziałało. Ref: jQLite
Bilal Mirza
3
Wprowadzenie dyrektywy w sprawie takich prostych kwestii wydaje się nieco rozdęte, jeśli mnie zapytasz ... Sprawdź poniżej odpowiedź Chrisa
Wilt
54

Chociaż Renaud dał świetne rozwiązanie

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

Osobiście odkryłem, że potrzebujesz również $ event.stopPropagation () w niektórych przypadkach, aby uniknąć niektórych skutków ubocznych

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

będzie moim rozwiązaniem

zainengineer
źródło
34
ng-click="$event.preventDefault()"
The Whiz of Oz
źródło
13
To najlepsze rozwiązanie. Oczywiście możesz również przekazać obiekt zdarzenia $ do funkcji zakresu. Podobnie jak ng-click = "myFunction ($ event, otherParams), a następnie $ event.preventDefault () w myFunction. Toczenie własnej dyrektywy w tym celu to przesada
Charlie Martin
34

Najłatwiejsze rozwiązanie, jakie znalazłem, to:

<a href="#" ng-click="do(); $event.preventDefault()">Click</a>
Clément Renaud
źródło
Jest to nie tylko najłatwiejsze, ale także nie łączy zdarzeń ze sterownikiem, jak sugerowano w innych rozwiązaniach. Łączenie zdarzeń ze sterownikiem jest czymś, czego należy unikać za wszelką cenę, ponieważ znacznie utrudnia testowanie.
jornare
11

Więc czytając te odpowiedzi, @Chris nadal ma najbardziej „poprawną” odpowiedź, jak sądzę, ale ma jeden problem, nie pokazuje „wskaźnika” ....

Oto dwa sposoby rozwiązania tego problemu bez konieczności dodawania kursora: styl wskaźnika:

  1. Użyj javascript:void(0)zamiast #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. Użyj $event.preventDefault()w ng-clickdyrektywie (aby nie blokować kontrolera referencjami związanymi z DOM):

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

Osobiście wolę ten pierwszy od drugiego. javascript:void(0)ma inne zalety omówione tutaj . Dyskusja o „dyskretnym JavaScript” w tym linku, który jest przerażająco nowy i niekoniecznie dotyczy bezpośrednio aplikacji kątowej.

Ben Lesh
źródło
2
Dlaczego jest to zaniżone? Zauważyłem również, że wskaźnik nie pokazuje się z odpowiedzią Chrisa.
Wim Deblauwe
2
javascript: void (0) jest znacznie lepszy niż #, który może działać na trasie, jeśli źle ją skonfigurujesz. +1
Shay Elkayam
2
Właśnie miałem napisać odpowiedź na to. Możesz także zrobićjavascript:;
Adrian
10

Możesz wykonać następujące czynności

1. Usuń hrefatrybut ze aznacznika anchor ( )

2.Ustaw kursor kursora w css, aby kliknąć elementy

 [ng-click],
 [data-ng-click],
 [x-ng-click] {
     cursor: pointer;
 }
Fizer Khan
źródło
9

HTML

tutaj pure angularjs: w pobliżu funkcji ng-click możesz napisać funkcję preventDefault () oddzielając średnik

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

JS

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

(lub)

możesz postępować w ten sposób, ktoś już o tym tutaj dyskutuje.

HTML

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

JS

$scope.do = function(event) {
    event.preventDefault();
    event.stopPropagation()
}
vallepu veerendra kumar
źródło
7

Wypróbuj tę opcję, która nie jest jeszcze wymieniona powyżej:

<a href="" ng-click="do()">Click</a>
pollux1er
źródło
6

Skoro tworzysz aplikację internetową, dlaczego potrzebujesz linków?

Zamień kotwice na guziki!

<button ng-click="do()"></button>
Sidonaldson
źródło
2
Ponieważ wen aplikacje internetowe nie mogą mieć kotwic?
Robin van Baalen
1
Mówiłem o tym, że większość aplikacji internetowych nie potrzebuje linków względnych, tak jak strony internetowe, często używają też linków zakotwiczenia jako wyzwalaczy javascript i nie linkują do strony; dlatego przycisk z domyślnym resetem stylu jest tak samo semantycznie poprawny, jeśli nie bardziej.
sidonaldson
3
@sidonaldson W rzeczywistości wiele dzisiejszych aplikacji internetowych w dużej mierze zależy od linków. Spójrz na routing angularjs.
Robert
1
@Robert tak, ale jeśli korzystasz z wbudowanego routingu, nie musisz zarządzać zapobieganiem domyślnym, jest to zrobione za Ciebie. Zakładam, że skoro plakat chce anulować link kotwiczny, mówi o linkach innych niż trasy. Na przykład uruchomienie modalne itp.
Sidonaldson
2
Ta odpowiedź powinna mieć o wiele więcej głosów pozytywnych. W wielu przypadkach, jeśli chcesz zapobiec domyślnemu zachowaniu zakotwiczenia, używasz zakotwiczenia jako przycisku, a nie łącza.
ItsCosmo,
6

jeśli nadal dotyczy:

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

...

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

...
xac
źródło
6

Najbezpieczniejszym sposobem uniknięcia zdarzeń na hrefie byłoby zdefiniowanie go jako

<a href="javascript:void(0)" ....>
Michael Drob
źródło
5

Poszedłbym z:

<a ng-click="do()">Click</a>
  • ponieważ zgodnie z dokumentami powinieneś być w stanie wyjść z href, a następnie Angular zajmie się domyślnym zapobieganiem!

Całe to zapobieganie domyślnej rzeczy było dla mnie mylące, więc stworzyłem JSFiddle tam, aby zilustrować, kiedy i gdzie Angular uniemożliwia domyślne .

JSFiddle używa dyrektywy Angulara - więc powinno być DOKŁADNIE takie samo. Możesz zobaczyć kod źródłowy tutaj: kod źródłowy tagu

Mam nadzieję, że pomoże to niektórym wyjaśnić.

Chciałbym opublikować dokument w ngHref, ale nie mogę z powodu mojej reputacji.

martinmose
źródło
5

Tak zawsze robię. Działa jak marzenie!

<a href ng-click="do()">Click</a>
Kocia ryba
źródło
4

Lub jeśli potrzebujesz inline, możesz to zrobić:

<a href="#" ng-click="show = !show; $event.preventDefault()">Click to show</a>
ntekka
źródło
4

Wiele odpowiedzi wydaje się przesadnych. DLA MNIE działa to

 <a ng-href="#" ng-click="$event.preventDefault();vm.questionContainer($index)">{{question.Verbiage}}</a>
Tom Stickel
źródło
2

Potrzebuję obecności wartości atrybutu href do degradacji (gdy js jest wyłączony), więc nie mogę użyć pustego atrybutu href (lub „#”), ale powyższy kod nie działał dla mnie, ponieważ potrzebuję zdarzenia ( e) zmienna. Stworzyłem własną dyrektywę:

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

W HTML:

<a data-click-prevent="true" href="/users/sign_up" ng-click="openSignUpModal()">Sign up</a>
skoncentrowany
źródło
2

Pożyczanie od odpowiedzi Tennisgent. Podoba mi się, że nie musisz tworzyć niestandardowej dyrektywy, aby dodać wszystkie linki. Nie mogłem jednak zmusić go do pracy w IE8. Oto, co w końcu dla mnie zadziałało (używając angular 1.0.6).

Zauważ, że 'bind' pozwala ci używać jqLite dostarczanego przez angular, więc nie musisz owijać pełnego jQuery. Wymagana również metoda 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();
                    }
                })
            }
        };
    }
])
Lukus
źródło
3
To zabija za dużo. Na przykład, kiedy używam Bootstrap na Twitterze dropdown, chcę propagacji, ale nie domyślnego zachowania. Ten fragment kodu NIE jest zalecany.
Robin van Baalen
2

Zetknąłem się z tym samym problemem podczas używania kotwic do rozwijania kątowego paska startowego. Jedynym rozwiązaniem, które zauważyłem, że uniknęło niepożądanych efektów ubocznych (tj. Rozwijanie się nie zamyka z powodu użycia funkcji preventDefault ()), było zastosowanie następujących czynności:

 <a href="javascript:;" ng-click="do()">Click</a>
cjackson
źródło
2

jeśli nigdy nie chcesz iść do href ... powinieneś zmienić swój znacznik i użyć przycisku, a nie tagu kotwicy, ponieważ semantycznie nie jest to kotwica ani link. Odwrotnie, jeśli masz przycisk, który wykonuje pewne kontrole, a następnie przekierowuje, powinien to być link / tag zakotwiczenia, a nie przycisk ... do celów SEO, a także do testowania [wstaw tutaj pakiet testowy].

dla linków, które chcesz tylko warunkowo przekierować, takich jak monit o zapisanie zmian lub potwierdzenie stanu nieczystości ... powinieneś użyć ng-click = "someFunction ($ event)", a w someFunction w swojej walidacjiError zapobiegaj domyślnej i stopPropagacji.

również dlatego, że możesz, nie znaczy, że powinieneś . javascript: void (0) działał dobrze w ciągu dnia ... ale z powodu nikczemnych hakerów / crackerów przeglądarki oznaczają aplikacje / witryny, które używają javascript w href ... nie widzę powodu, aby unikać tego typu ..

jest rok 2016, ponieważ 2010 nie powinien być nigdzie powodem do używania linków, które działają jak przyciski ... jeśli nadal musisz obsługiwać IE8, a poniżej musisz ponownie ocenić swoje miejsce prowadzenia działalności.

PDA
źródło
1
/* NG CLICK PREVENT DEFAULT */

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

źródło
1

To, co powinieneś zrobić, to hrefcałkowicie pominąć atrybut.

Jeśli spojrzeć na kod źródłowy do adyrektywy element (który jest częścią kątowego rdzenia), stwierdza się na linii 29 - 31:

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

Co oznacza, że ​​Angular już rozwiązuje problem linków bez href. Jedyny problem, który nadal masz, to problem z css. Nadal możesz zastosować styl wskaźnika do kotwic, które mają kliknięcia ng, np .:

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

Możesz nawet zwiększyć dostępność witryny, zaznaczając prawdziwe linki subtelną, inną stylizacją niż linki wykonujące funkcje.

Miłego kodowania!

Justus Romijn
źródło
1

Możesz wyłączyć przekierowanie adresu URL wewnątrz Html5Mode $ location, aby osiągnąć cel. Możesz go zdefiniować w określonym kontrolerze używanym dla strony. Coś takiego:

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

Dziarskość
źródło
1

Jeśli używasz Angulara 8 lub więcej, możesz utworzyć dyrektywę:

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

}

Na tagu zakotwiczonym w komponencie możesz go połączyć w następujący sposób:

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

Moduł aplikacji powinien mieć deklarację:

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


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective
Raghav
źródło
-1

Alternatywą może być:

<span ng-click="do()">Click</span>
Terence Bandoian
źródło
1
Okropną praktyką jest wykorzystywanie spando celów klikania. Po prostu skorzystaj z odpowiedzi @ tennisgent - kotwice hrefniezdefiniowane.
Robin van Baalen
1
@RobinvanBaalen Element span został zdefiniowany jako klikalny od co najmniej HTML 4.0.1: w3.org/TR/html401/struct/global.html#edef-SPAN w3.org/TR/html5/dom.html#global-attributes html.spec.whatwg.org/multipage/dom.html#global-attributes
Terence Bandoian
1
Jeśli wykonujesz <span> Cześć </span> od kiedy to można kliknąć? Specyfikacja prawdopodobnie mówi, że <span> może być używany wewnątrz klikalnego elementu, takiego jak <a> lub <przycisk>. Ale z drugiej strony można w ten sposób kliknąć wszystkie wbudowane elementy blokowe (img, span itp.). Sam zakres nie jest klikalny.
Robin van Baalen,
1
@RobinvanBaalen Proszę zobaczyć linki powyżej. Atrybut onclick dla elementów zakresu został zdefiniowany od co najmniej HTML 4.01.
Terence Bandoian
1
Nigdy nie mówiłem, że dodanie modułu obsługi zdarzeń nie zadziała dla zakresu. Powiedziałem, że złą praktyką jest tworzenie elementów niemożliwych do kliknięcia, takich jak span, div, section, ul, li itp., Klikalne za pomocą procedury obsługi zdarzeń javascript. Chodzi o semantykę. Semantyka w HTML to praktyka nadawania treści znaczeniom i strukturze strony przy użyciu odpowiedniego elementu.
Robin van Baalen,