Como evitar o padrão nas tags de âncora?

342

Digamos que eu tenho uma tag de âncora como

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

Como impedir que o navegador navegue para # no AngularJS ?

Micael
fonte
14
Como Chris diz abaixo, deixe de fora o href.
Jesse
13
Não é isso que Chris diz, Jesse, usa href=''para manter o comportamento do ponteiro do mouse.
Oma #
11
Você acabou de remover o sinal # do href, tudo bem.
HENG Vongkol
4
Ou apenas use href = "javascript: void (0);" para manter o ponteiro, mas não têm ação (até que você adicionar outro manipulador de clique)
Robba
11
Você poderia alterar a resposta aceita da melhor e mais votada por Chris - stackoverflow.com/a/11672909 ?
Piotr Dobrogost

Respostas:

259

ATUALIZAÇÃO : Desde então, mudei de idéia sobre esta solução. Após mais desenvolvimento e tempo gasto trabalhando nisso, acredito que uma solução melhor para esse problema é fazer o seguinte:

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

E atualize seu csspara ter uma regra extra:

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

É muito mais simples e oferece exatamente a mesma funcionalidade e é muito mais eficiente. Espero que possa ser útil para quem procura essa solução no futuro.


A seguir, minha solução anterior, que deixo aqui apenas para fins de legado:

Se você está tendo esse problema muito, uma diretiva simples que corrija esse problema é a seguinte:

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

Ele verifica todas as tags de âncora ( <a></a>) para ver se seu hrefatributo é uma string vazia ( "") ou um hash ( '#') ou se há uma ng-clickatribuição. Se encontrar alguma dessas condições, ele captura o evento e evita o comportamento padrão.

A única desvantagem é que ele executa essa diretiva para todas as tags âncora. Portanto, se você possui muitas tags âncora na página e deseja apenas impedir o comportamento padrão de um pequeno número delas, essa diretiva não é muito eficiente. No entanto, quase sempre quero preventDefault, então uso essa diretiva em todos os meus aplicativos AngularJS.

tenente
fonte
2
Obrigado @matejkramny, críticas muito construtivas. Alguma sugestão para torná-lo menos "bagunçado"?
tennisgent
11
Conforme indicado em outras respostas, ter um hrefatributo em branco tem comportamento variável em diferentes navegadores. Embora eu concorde que seja de longe a solução mais limpa e eficiente, ela tem alguns problemas entre navegadores. O uso da abordagem de diretiva permite que o navegador manipule a <a>tag como faria normalmente, mas ainda obtendo ao OP a resposta que estava procurando.
tennisgent
2
Isso funciona e quase que é um exagero grave para um problema muito simples. Angular fornece acesso ao objeto de evento com $ event, para que você possa fazer exatamente o que faria em eventos simples js ou jquery click. Veja esta resposta stackoverflow.com/a/19240232/1826354 e meu comentário sobre ele
Charlie Martin
11
Sim. Você pode. Há duas outras respostas neste post que já fazem o que você está descrevendo. E eu concordo, é um exagero. Foi por isso que fiz a atualização que fiz.
tennisgent 14/02
6
Isso é bom se você não se importa com a acessibilidade no seu site. Ao deixar de fora o href, não é possível usar o teclado para tabular esse item. A menos que você tenha adicionado um índice de tabulação. Com tudo isso em mente, por que não usar preventDefault ...? É para isso que existe.
precisa saber é o seguinte
319

De acordo com os documentos para ngHref, você deve deixar o href ou fazer 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
fonte
6
É a única boa resposta real. Qualquer coisa que exige tocar o DOM ou eventos nativos é questionável
vincent
4
Essa é a resposta correta. Se você deixar cair href das AngularJS atributo <a> chamará evitar default:
pkozlowski.opensource
25
A única desvantagem de deixar de fora o atributo href é que você perde o cursor 'ponteiro' normal ao passar o mouse sobre um link. Você pode corrigir isso adicionando uma regra à sua folha de estilo: a: hover {cursor: pointer; }
karlgold
35
Lembre-se de que isso também torna a tag desmarcável, o que não é muito fácil de acessar.
Richard Szalay
3
Para mim, o navegador ainda interrompe a ação do clique: /
Matej 26/02
238

Você pode passar o objeto $ event para o seu método e invocá $event.preventDefault()-lo, para que o processamento padrão não ocorra:

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

// then in your controller.do($event) method
$event.preventDefault()
mna
fonte
13
sim, e você também pode chamar $ event.stopPropagation () para que o evento não borbulhe (basicamente, você terá acesso ao objeto Event conforme definido pelo W3C: w3.org/TR/DOM-Level-2- Events / events.html # Events-Event )
mna
11
Observe que no momento em que isso foi escrito, deixar o href vazio (ou ausente) não funcionava no IE8 / 9 e no Opera. Isso foi há um ano e não tive a chance de tentar novamente (abri um problema na época no Github, mas acho que eles redefiniram os problemas há algum tempo). Se isso for corrigido (ou você não se importa com esses navegadores), use a resposta de Chris! Se alguém puder experimentar e comentar / editar a resposta, melhor ainda.
mna
11
@PuerkitoBio - Posso confirmar que o IE8 e abaixo ainda exigem $event.preventDefault()... IE tax.
22713 Scotty.NET
4
passar o evento $ para o controlador significa referenciar o DOM no seu controlador, o que significa que você acoplou sua visualização e seu controlador. Você pode chamar os métodos $ event diretamente na sua expressão avaliada: ng-click="do(); $event.preventDefault()por exemplo ... embora não seja necessário porque a resposta de @ Chris abaixo é a correta. Isso pode ajudar as pessoas em situações nas quais eles aninharam ngClicks e desejam chamar $event.stopPropagation(), no entanto.
Ben Lesh
2
Finalmente, uma solução amigável para SEO. Você também pode atualizar o URL do navegador sem recarregar a ng-view - joelsaupe.com/programming/… Dessa forma, você possui links que não recarregam sua visualização, mas podem ser abertos em uma nova guia e os mecanismos de pesquisa podem segui-los. YAY!
Tom
111

Eu prefiro usar diretivas para esse tipo de coisa. Aqui está um exemplo

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

E o código de diretiva para eat-click:

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

Agora você pode adicionar o eat-clickatributo a qualquer elemento e ele será preventDefault()editado automaticamente.

Benefícios:

  1. Você não precisa passar o $eventobjeto feio para sua do()função.
  2. Seu controlador é mais testável por unidade, porque não precisa remover o $eventobjeto
djsmith
fonte
11
O @Kato Angular vem com seu próprio subconjunto de jQuery, para facilitar nossa vida.
Neil
3
Benefício adicional - isso também funciona no IE8 e abaixo, se isso for importante para você.
21913 Scotty.NET
11
Eu estou recebendo Error: $ is not defined. o que estou perdendo?
Bilal Mirza
7
Eu tentei angular.element(element).bind('click', function(event){...});. Funcionou. Re
Bilal Mirza
3
Apresentando uma directiva para simples algo como isso parece um pouco inchado, se você me perguntar ... Entrada Chris sua resposta abaixo
Wilt
54

Embora Renaud tenha dado uma ótima solução

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

Pessoalmente, achei que você também precisa de $ event.stopPropagation () em alguns casos, para evitar alguns dos efeitos colaterais

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

será minha solução

zainengineer
fonte
34
ng-click="$event.preventDefault()"
O Whiz de Oz
fonte
13
essa é a melhor solução. Obviamente, você também pode passar o objeto $ event para sua função de escopo. . Como ng-click = "myFunction ($ event, otherParams) e, em seguida $ event.preventDefault () em myFunction Rolling seu próprio directiva para isso é um exagero
Charlie Martin
34

A solução mais fácil que encontrei é esta:

<a href="#" ng-click="do(); $event.preventDefault()">Click</a>
Clément Renaud
fonte
Isso não é apenas o mais fácil, mas também não combina eventos com o controlador, como sugerido em outras soluções. Acoplar eventos a um controlador é algo que você deve evitar a todo custo, pois torna o teste muito mais difícil.
jornare
11

Então, lendo essas respostas, o @Chris ainda tem a resposta mais "correta", suponho, mas tem um problema, não mostra o "ponteiro" ....

Então, aqui estão duas maneiras de resolver esse problema sem precisar adicionar um cursor: estilo do ponteiro:

  1. Use em javascript:void(0)vez de #:

    <a href="javascript:void(0)" ng-click="doSomething()">Do Something</a>
  2. Use $event.preventDefault()na ng-clickdiretiva (para não desperdiçar seu controlador com referências relacionadas ao DOM):

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

Pessoalmente, prefiro o primeiro do que o último. javascript:void(0)tem outros benefícios discutidos aqui . Há também discussões sobre "JavaScript discreto" nesse link assustadoramente recente e que não se aplica necessariamente diretamente a um aplicativo angular.

Ben Lesh
fonte
2
Por que isso é prejudicado? Notei também que o ponteiro não está aparecendo com a resposta de Chris.
Wim Deblauwe
2
javascript: void (0) é muito melhor que #, que pode atuar na rota se você configurá-la incorretamente. 1
Shay Elkayam
2
Eu estava prestes a escrever uma resposta para isso. Você também pode fazerjavascript:;
Adrian
10

Você pode fazer o seguinte

1.Remova o hrefatributo da amarca anchor ( )

2. Defina o cursor do ponteiro em css para ng clique em elementos

 [ng-click],
 [data-ng-click],
 [x-ng-click] {
     cursor: pointer;
 }
Fizer Khan
fonte
9

HTML

aqui angularjs puros: perto da função ng-click, você pode escrever a função preventDefault () separando ponto e vírgula

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

JS

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

(ou)

você pode continuar dessa maneira, isso já é discutido aqui.

HTML

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

JS

$scope.do = function(event) {
    event.preventDefault();
    event.stopPropagation()
}
vallepu veerendra kumar
fonte
7

Tente esta opção que eu posso ver ainda não está listada acima:

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

Como você está criando um aplicativo Web, por que precisa de links?

Troque suas âncoras para botões!

<button ng-click="do()"></button>
sidonaldson
fonte
2
Como os aplicativos da Web não podem ter âncoras?
Robin van Baalen
11
Eu estava me referindo ao ponto em que a maioria dos aplicativos da web não precisa de links relativos, como os sites, mas também costuma usar links de âncora como acionadores de javascript e não vincula a uma página; portanto, um botão com a redefinição de estilo padrão é igualmente semanticamente correta, se não mais.
Sidneyson
3
@sidonaldson Na verdade, muitos aplicativos da web hoje em dia dependem fortemente de links. Veja o roteamento do angularjs.
Robert
11
@ Robert sim, mas se você usar o roteamento interno, não precisará gerenciar para evitar o padrão, isso é feito para você. Presumo que o pôster queira cancelar um link âncora que ele está falando sobre links que não são de rota. Por exemplo, iniciando um modal etc #
sidonaldson
2
Essa resposta deve ter muito mais votos positivos. Em muitos casos, se você deseja impedir o comportamento padrão da âncora, está usando a âncora como um botão e não como um link.
ItsCosmo
6

se ainda relevante:

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

...

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

...
xac
fonte
6

A maneira mais segura de evitar eventos em um href seria defini-lo como

<a href="javascript:void(0)" ....>
Michael Drob
fonte
5

Eu iria com:

<a ng-click="do()">Click</a>
  • porque de acordo com os documentos, você deve poder sair do href e o Angular cuidará do padrão de prevenção para você!

Toda essa coisa de impedir o padrão foi confusa para mim, então eu criei um JSFiddle lá para ilustrar quando e onde o Angular está impedindo o padrão .

O JSFiddle está usando uma diretiva angular - portanto deve ser EXATAMENTE o mesmo. Você pode ver o código fonte aqui: um código fonte de tag

Espero que isso ajude a esclarecer alguns.

Eu gostaria de postar o documento no ngHref, mas não posso por causa da minha reputação.

martinmose
fonte
5

É isso que eu sempre faço. Funciona como um encanto!

<a href ng-click="do()">Click</a>
Peixe-gato
fonte
4

Ou, se você precisar do inline, poderá fazer o seguinte:

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

Muitas respostas parecem ser um exagero. Para mim, isso funciona

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

Eu preciso da presença do valor do atributo href para degradação (quando js está desativado), portanto não posso usar o atributo href vazio (ou "#"), mas o código acima não funcionou para mim, porque preciso de um evento ( e) variável. Eu criei minha própria diretiva:

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

Em HTML:

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

Tomando emprestado a resposta de tennisgent. Eu gosto que você não precisa criar uma diretiva personalizada para adicionar todos os links. No entanto, eu não consegui fazê-lo trabalhar no IE8. Aqui está o que finalmente funcionou para mim (usando o angular 1.0.6).

Observe que 'bind' permite que você use o jqLite fornecido pelo angular, portanto não há necessidade de envolver o jQuery completo. Também é necessário o 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();
                    }
                })
            }
        };
    }
])
Lukus
fonte
3
Isso mata muito. Por exemplo, quando eu uso o Twitter Bootstrap dropdown, quero a propagação, mas não o comportamento padrão. Este trecho NÃO é recomendado.
Robin van Baalen
2

Corri para esse mesmo problema ao usar âncoras para uma lista de inicialização angular suspensa. A única solução que descobri que evitou efeitos colaterais indesejados (ou seja, a lista suspensa que não fechava por causa do preventDefault ()) era usar o seguinte:

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

se você nunca quiser acessar o href ... altere sua marcação e use um botão que não seja uma marca de âncora, porque semanticamente não é uma âncora ou um link. Inversamente, se você tem um botão que faz algumas verificações e depois o redireciona, deve ser uma tag de link / âncora e não um botão ... para fins de SEO e testes [insira o conjunto de testes aqui].

para links que você deseja redirecionar apenas condicionalmente, como solicitar salvar alterações ou reconhecer estado sujo ... você deve usar ng-click = "someFunction ($ event)" e em someFunction em seu validationError preventDefault e ou stopPropagation.

também só porque você pode, não significa que deveria . javascript: void (0) funcionou bem antigamente ... mas por causa dos nefastos navegadores de hackers / crackers sinalizam aplicativos / sites que usam javascript dentro de um href ... não há motivo para digitar acima ..

é 2016 desde 2010, não deve haver razão para usar links que funcionam como botões ... se você ainda precisa dar suporte ao IE8 e abaixo, precisa reavaliar seu local de negócios.

PDA
fonte
1
/* NG CLICK PREVENT DEFAULT */

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

fonte
1

O que você deve fazer é omitir o hrefatributo completamente.

Se você olhar para o código-fonte da adiretiva de elemento (que faz parte do núcleo Angular), ela declara nas linhas 29 a 31:

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

O que significa que o Angular já está resolvendo o problema de links sem um href. O único problema que você ainda tem é o problema de css. Você ainda pode aplicar o estilo do ponteiro às âncoras com cliques em ng, por exemplo:

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

Assim, você pode tornar seu site mais acessível, marcando links reais com um estilo sutil e diferente dos links que executam funções.

Feliz codificação!

Justus Romijn
fonte
1

Você pode desativar o redirecionamento de URL no Html5Mode de $ location para atingir o objetivo. Você pode defini-lo dentro do controlador específico usado para a página. Algo assim:

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

Traço
fonte
1

Se você estiver usando o Angular 8 ou mais, poderá criar uma diretiva:

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 sua âncora no componente, você pode conectá-lo da seguinte maneira:

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

O módulo de aplicativo deve ter sua declaração:

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


@NgModule({
  declarations: [
    AppComponent,
    PreventDefaultDirective
Raghav
fonte
-1

Uma alternativa pode ser:

<span ng-click="do()">Click</span>
Terence Bandoian
fonte
11
É uma prática horrível abusar de uma spanpara clicar. Basta ir para a resposta do @ tennisgent - âncoras sem hrefdefinição.
Robin van Baalen
11
@RobinvanBaalen O elemento span foi definido como clicável desde pelo menos o 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
11
Se você der um <span> Olá </span> desde quando isso é clicável? O que a especificação provavelmente diz é que um <span> pode ser usado dentro de um elemento clicável como <a> ou <button>. Mas, novamente, todos os elementos de bloco em linha (img, span, etc) podem ser clicados dessa maneira. Uma extensão em si não é clicável.
Robin van Baalen
11
@RobinvanBaalen Por favor, veja os links acima. O atributo onclick para elementos de span foi definido desde pelo menos HTML 4.01.
Terence Bandoian
11
Eu nunca disse que adicionar um manipulador de eventos não funcionaria por um período. Eu disse que é uma prática ruim tornar um elemento não clicável, como span, div, seção, ul, li, etc. clicável usando um manipulador de eventos javascript. É tudo sobre semântica. A semântica no HTML é a prática de fornecer conteúdo à estrutura e significado da página usando o elemento apropriado.
Robin van Baalen