Как предотвратить дефолт по тегам привязки?


342

Допустим, у меня есть тег привязки, такой как

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

Как я могу запретить браузеру переходить на # в AngularJS ?


14
Как говорит Крис ниже, просто опустите href.
Джесси

13
Это не то, что говорит Крис, Джесси, href=''чтобы сохранить поведение указателя мыши.
Ома

1
Вы просто удалите знак # с href, это нормально.
ХЕНГ Вонгкол

4
Или просто используйте href = "javascript: void (0);" удерживать указатель, но не предпринимать никаких действий (пока вы не добавите еще один обработчик кликов)
Робба

1
Не могли бы вы поменять принятый ответ на лучший и получивший наибольшее количество голосов ответ Криса - stackoverflow.com/a/11672909 ?
Петр Доброгост

Ответы:


259

ОБНОВЛЕНИЕ : с тех пор я передумал об этом решении. Я полагаю, что после долгих разработок и времени, потраченного на работу над этим, лучшее решение этой проблемы заключается в следующем:

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

А затем обновите свой, cssчтобы иметь дополнительное правило:

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

Это гораздо проще и обеспечивает точно такую ​​же функциональность и гораздо эффективнее. Надеюсь, что это может быть полезно для всех, кто ищет это решение в будущем.


Ниже приведено мое предыдущее решение, которое я оставляю здесь только для унаследованных целей:

Если вы часто сталкиваетесь с этой проблемой, простая директива, которая решит эту проблему, будет следующей:

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

Он проверяет все теги привязки ( <a></a>), чтобы hrefопределить, является ли их атрибут пустой строкой ( "") или хешем ( '#'), или есть ng-clickприсваивание. Если он находит какое-либо из этих условий, он ловит событие и предотвращает поведение по умолчанию.

Единственным недостатком является то, что он выполняет эту директиву для всех тегов привязки. Так что если у вас много якорных тегов на странице, и вы хотите предотвратить поведение по умолчанию только для небольшого их числа, то эта директива не очень эффективна. Тем не менее, я почти всегда хочу preventDefault, поэтому я использую эту директиву повсюду в моих приложениях AngularJS.


2
Спасибо @matejkramny, очень конструктивная критика. Любые предложения, чтобы сделать его менее "грязным"?
теннисный агент

1
Как указывалось в других ответах, наличие пустого hrefатрибута в разных браузерах различно. Хотя я согласен, что это, безусловно, самое чистое и эффективное решение, у него есть некоторые проблемы с кроссбраузерностью. Использование директивного подхода позволяет браузеру обрабатывать <a>тэг, как обычно, но все же получает OP ответ, который они искали.
теннисный агент

2
Это работает, и все, кроме серьезного перегиба для очень простой проблемы. Angular дает вам доступ к объекту события с помощью $ event, так что вы можете делать именно то, что вы делаете в обычных событиях js или jquery click. Смотрите этот ответ stackoverflow.com/a/19240232/1826354 и мой комментарий к нему
Чарли Мартин

1
Ага. Ты можешь. В этом посте есть еще два ответа, которые уже делают то, что вы описываете. И я согласен, это излишне. Вот почему я сделал обновление, которое я сделал.
теннисный агент

6
Это хорошо, если вы не заботитесь о доступности вашего сайта. Если не указывать ссылку, невозможно использовать клавиатуру для перехода на этот элемент. Если вы не добавили tabindex. Имея все это в виду, почему бы просто не использовать protectDefault ...? Вот для чего он там.
duhseekoh

319

В соответствии с документами для ngHref, вы должны быть в состоянии отказаться от href или сделать 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
Это единственный действительно хороший ответ. Все, что требует прикосновения к DOM или родным событиям, сомнительно
Винсент

4
Это правильный ответ. Если вы удалите href из атрибута <a>, AngularJS вызовет предотвращение по умолчанию:
pkozlowski.opensource

25
Единственный недостаток исключения атрибута href - это то, что вы теряете обычный курсор-указатель при наведении курсора на ссылку. Это можно исправить, добавив правило в таблицу стилей: a: hover {cursor: pointer; }
karlgold

35
Имейте в виду, что это также делает тег недоступным для вкладок, что не очень удобно для пользователей.
Ричард Сзалай

3
Для меня браузер все еще раздувает действие клика: /
Matej

238

Вы можете передать объект $ event вашему методу и вызвать $event.preventDefault()его, чтобы обработка по умолчанию не происходила:

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

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

13
да, и вы также можете вызвать $ event.stopPropagation (), чтобы событие не всплыло (в основном, у вас есть доступ к объекту Event, как определено в W3C: w3.org/TR/DOM-Level-2- События / events.html # Событийный Event )
МНА

1
Обратите внимание, что в то время, когда это было написано, href пустой (или отсутствующий) не работал на IE8 / 9 и Opera. Это было год назад, и у меня не было возможности попробовать это снова (я тогда открыл проблему на Github, но я думаю, что они сбросили проблемы некоторое время назад). Если это исправлено (или вам не нужны эти браузеры), то непременно воспользуйтесь ответом Криса! Если кто-то может попробовать и прокомментировать / отредактировать ответ, даже лучше.
MNA

1
@PuerkitoBio - я могу подтвердить, что IE8 и ниже все еще требует $event.preventDefault()... IE налог.
Scotty.NET

4
Передача события $ в контроллер означает обращение к DOM в вашем контроллере, что означает, что вы соединили свое представление и ваш контроллер. Вы можете вызывать методы $ event непосредственно в вашем оцененном выражении: ng-click="do(); $event.preventDefault()например ... хотя, в этом нет необходимости, потому что приведенный ниже ответ @ Chris правильный. Это может помочь людям в ситуациях, когда они вложили ngClicks и хотят звонить $event.stopPropagation().
Бен Леш

2
Наконец, SEO дружественное решение. Возможно, вы также захотите обновить URL браузера, не перезагружая ng-view - joelsaupe.com/programming/… Таким образом, у вас есть ссылки, которые не перезагружают ваше представление, но могут быть открыты в новой вкладке, и поисковые системы могут следить за ними. УРА!
Том

111

Я предпочитаю использовать директивы для такого рода вещей. Вот пример

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

И код директивы для eat-click:

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

Теперь вы можете добавить eat-clickатрибут к любому элементу, и он будет preventDefault()обработан автоматически .

Преимущества:

  1. Вам не нужно передавать уродливый $eventобъект в вашу do()функцию.
  2. Ваш контроллер более тестируем, потому что ему не нужно заглушать $eventобъект

1
@Kato Angular поставляется с собственным набором jQuery, чтобы сделать нашу жизнь проще.
Нил

3
Дополнительное преимущество - это также работает в IE8 и ниже, если это важно для вас.
Scotty.NET

1
Я получаю Error: $ is not defined. Чего мне не хватает?
Билал Мирза

7
Я попробовал angular.element(element).bind('click', function(event){...});. Это сработало. Ref: jQLite
Билал Мирза

3
Представление директивы для чего-то простого, как это, кажется немного раздутым, если вы спросите меня ... Посмотрите на его ответ Криса ниже
Уилт

54

Хотя Рено дал отличное решение

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

Я лично обнаружил, что в некоторых случаях вам также нужно $ event.stopPropagation (), чтобы избежать побочных эффектов.

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

будет моим решением


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

13
Это лучшее решение. Конечно, вы могли бы также передать объект $ event вашей функции видимости. Как ng-click = "myFunction ($ event, otherParams), а затем $ event.preventDefault () в myFunction. Откатить собственную директиву для этого - излишество
Чарли Мартин,

34

Самое простое решение, которое я нашел, это:

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

Это не только самое простое, но и не связывает события с контроллером, как предлагалось в других решениях. Связывание событий с контроллером - это то, чего вам следует избегать любой ценой, поскольку это значительно усложняет тестирование.
Jornare

11

Итак, читая эти ответы, у @Chris все еще есть самый «правильный» ответ, я полагаю, но у него есть одна проблема, он не показывает «указатель» ....

Итак, есть два способа решения этой проблемы без добавления курсора: стиль указателя:

  1. Используйте javascript:void(0)вместо #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. Используйте $event.preventDefault()в ng-clickдирективе (чтобы не сворачивать контроллер с ссылками, связанными с DOM):

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

Лично я предпочитаю первое, а не второе. javascript:void(0)имеет другие преимущества, которые обсуждаются здесь . В этой ссылке также обсуждается «ненавязчивый JavaScript», который пугающе недавний и не обязательно имеет прямое отношение к угловому приложению.


2
Почему это понижено? Я также заметил, что указатель не отображается с ответом от Криса.
Вим Deblauwe

2
javascript: void (0) намного лучше, чем #, который может действовать на маршруте, если вы его неправильно настроите. +1
Шей Элькаям

2
Я как раз собирался написать ответ на это. Вы также можете сделатьjavascript:;
Адриан

10

Вы можете сделать следующим образом

1. Удалить hrefатрибут с привязки (a тега )

2. Установите указатель курсора в CSS, чтобы щелкнуть элементы

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

9

HTML

здесь чисто angularjs: рядом с функцией ng-click вы можете написать функцию protectDefault (), разделив точку с запятой

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

JS

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

(или)

Вы можете продолжить этот путь, это уже обсуждалось здесь.

HTML

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

JS

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

7

Попробуйте этот вариант, который, как я вижу, еще не указан выше:

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

6

Поскольку вы создаете веб-приложение, зачем вам ссылки?

Поменяйте якоря на кнопки!

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

2
Раз уж у веба не может быть якорей?
Робин ван Баален

1
Я имел в виду тот факт, что большинству веб-приложений не нужны относительные ссылки, как веб-сайтам, они также часто просто используют якорные ссылки в качестве триггеров JavaScript и не ссылаются на страницу; поэтому кнопка со сбросом стиля по умолчанию так же семантически верна, если не больше.
Сидональдсон

3
@sidonaldson На самом деле, многие веб-приложения в настоящее время сильно зависят от ссылок. Посмотрите на маршрутизацию angularjs.
Роберт

1
@ Роберт, да, но если вы используете встроенную маршрутизацию, вам не нужно управлять предотвращением дефолта, это сделано для вас. Я предполагаю, что поскольку автор хочет отменить якорную ссылку, он говорит о ссылках вне маршрута. Например, запуск модального и т. Д.
sidonaldson

2
Этот ответ должен иметь гораздо больше голосов. Во многих случаях, если вы хотите предотвратить поведение привязки по умолчанию, вы используете привязку как кнопку, а не как ссылку.
ItsCosmo

6

если все еще актуально:

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

...

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

...

6

Самый безопасный способ избежать событий на href - это определить его как

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

5

Я бы пошел с:

<a ng-click="do()">Click</a>
  • потому что в соответствии с документами, вы должны быть в состоянии покинуть HREF, а затем Angular будет обрабатывать предотвращение по умолчанию для вас!

Вся эта штука с предотвращением по умолчанию сбивает меня с толку, поэтому я создал JSFiddle, чтобы проиллюстрировать, когда и где Angular предотвращает использование по умолчанию .

JSFiddle использует Angular как директиву, поэтому она должна быть точно такой же. Вы можете увидеть исходный код здесь: тег исходного кода

Я надеюсь, что это поможет разъяснения для некоторых.

Я хотел бы опубликовать документ на ngHref, но не могу из-за своей репутации.


5

Это то, что я всегда делаю. Работает как шарм!

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

4

Или, если вам нужен встроенный, то вы можете сделать это:

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

4

Многие ответы кажутся излишними. Для меня это работает

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

2

Мне нужно наличие значения атрибута href для ухудшения (когда js выключен), поэтому я не могу использовать пустой атрибут href (или "#"), но приведенный выше код не сработал для меня, потому что мне нужно событие ( д) переменная. Я создал свою собственную директиву:

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

В HTML:

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

2

Заимствование из ответа теннисиста. Мне нравится, что вам не нужно создавать пользовательские директивы для добавления на все ссылки. Однако я не мог заставить его работать в IE8. Вот что наконец-то сработало для меня (используя angular 1.0.6).

Обратите внимание, что «bind» позволяет вам использовать jqLite, предоставляемый angular, поэтому нет необходимости оборачивать его полным jQuery. Также требуется метод 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
Это убивает много. Например, когда я использую Twitter Bootstrap dropdown, я хочу распространение, но не поведение по умолчанию. Этот фрагмент НЕ рекомендуется.
Робин ван Баален

2

Я столкнулся с этой же проблемой, когда использовал якоря для выпадающего углового загрузчика. Единственное решение, которое я нашел, чтобы избежать нежелательных побочных эффектов (т. Е. Раскрывающийся список не закрывался из-за использования protectDefault ()), заключалось в следующем:

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

2

если вы никогда не хотите переходить к href ... вам следует изменить разметку и использовать кнопку, а не тег привязки, потому что семантически это не привязка или ссылка. И наоборот, если у вас есть кнопка, которая выполняет некоторые проверки, а затем перенаправляет ее, это должен быть тег ссылки / привязки, а не кнопка ... для целей SEO, а также для тестирования [вставьте набор тестов здесь].

для ссылок, которые вы хотите перенаправить только условно, например, запрос на сохранение изменений или подтверждение грязного состояния ... вы должны использовать ng-click = "someFunction ($ event)" и в someFunction для вашего validationError предотвращать дефолт и / или stopPropagation.

также только потому, что вы можете не означает, что вы должны . javascript: void (0) хорошо работал в те времена ... но из-за гнусных хакеров / взломщиков браузеры помечают приложения / сайты, которые используют javascript внутри href ... не вижу причин для ввода типа выше ...

2016 с 2010 года не должно быть поводом для использования ссылок, которые действуют как кнопки ... если вам все еще требуется поддержка IE8 и ниже, вам необходимо пересмотреть свое место работы.


1
/* NG CLICK PREVENT DEFAULT */

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

1

Что вы должны сделать, это полностью опустить hrefатрибут.

Если вы посмотрите на исходный код для aдирективы элемента (которая является частью ядра Angular), она указывает в строке 29–31:

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

Это означает, что Angular уже решает вопрос о ссылках без дополнительной информации. Единственная проблема, которая у вас осталась - это проблема css. Вы все еще можете применить стиль указателя к якорям, которые имеют ng-клики, например:

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

Таким образом, вы могли бы даже сделать свой сайт более доступным, пометив реальные ссылки тонким, отличающимся стилем от ссылок, которые выполняют функции.

Удачного кодирования!


1

Вы можете отключить перенаправление URL в Html5Mode $ location для достижения цели. Вы можете определить его внутри конкретного контроллера, используемого для страницы. Что-то вроде этого:

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


1

Если вы используете Angular 8 или более, вы можете создать директиву:

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

}

На вашем теге привязки к компоненту вы можете подключить его так:

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

Модуль приложения должен иметь свою декларацию:

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


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective

-1

Альтернативой может быть:

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

1
Это ужасная практика - злоупотреблять spanкликабельными целями. Просто зайдите на @ теннисный ответ - якоря без hrefопределения.
Робин ван Баален

1
@RobinvanBaalen Элемент span определен как интерактивный, начиная с версии 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
Теренс Бандоян

1
Если вы делаете <span> Hello </ span> с тех пор, когда это кликабельно? Спецификация, вероятно, говорит о том, что <span> может использоваться внутри кликабельного элемента, такого как <a> или <button>. Но опять же, все элементы встроенного блока (img, span и т. Д.) Могут быть сделаны кликабельными таким образом. Сам промежуток не кликабелен.
Робин ван Баален

1
@RobinvanBaalen Пожалуйста, смотрите ссылки выше. Атрибут onclick для элементов span определен как минимум с HTML 4.01.
Теренс Бандоян

1
Я никогда не говорил, что добавление обработчика событий не будет работать на промежуток времени. Я сказал, что это плохая практика - делать элемент без клика, например, span, div, section, ul, li и т. Д. Кликабельным, используя обработчик событий javascript. Это все о семантике. Семантика в HTML - это практика придания содержанию и смыслу структуры страницы с использованием соответствующего элемента.
Робин ван Баален
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.