Modificar uma string de consulta sem recarregar a página

114

Estou criando uma galeria de fotos e gostaria de poder alterar a string de consulta e o título quando as fotos forem navegadas.

O comportamento que estou procurando é frequentemente visto com algumas implementações de página contínua / infinita, onde enquanto você rola para baixo a string de consulta continua incrementando o número da página ( http://x.com?page=4 ) etc. Isso deve ser simples em teoria, mas eu gostaria de algo que fosse seguro nos principais navegadores.

Eu encontrei este ótimo post , e estava tentando seguir o exemplo com window.history.pushstate, mas isso não parece estar funcionando para mim. E não tenho certeza se é ideal porque realmente não me importo em modificar o histórico do navegador.

Só quero oferecer a capacidade de marcar a foto exibida no momento, sem recarregar a página toda vez que a foto for alterada.

Aqui está um exemplo de página infinita que modifica a string de consulta: http://tumbledry.org/

UPDATE encontrou este método:

window.location.href = window.location.href + '#abc';

parece funcionar para mim, mas estou em um novo cromo ... provavelmente causaria alguns problemas com navegadores mais antigos?

Sonic Soul
fonte
Você pode postar um link para algum site de exemplo que atualize sua string de consulta dinamicamente? Não acho que isso possa ser feito, mas você pode alterar o valor do hash e isso pode ser o suficiente para obter o que deseja.
Pointy de
possível duplicata de como manipular a URL com javascript / jquery?
Quentin de
possível duplicata de Como o GitHub muda a URL, mas não o recarrega?
Quentin de
possível duplicata de Por que o novo Dropbox da web pode alterar a URL sem atualizar a página? e as três perguntas são marcadas como uma duplicata de
Quentin
@Quentin. Você pode ter apenas um voto mais próximo ...:)
gdoron está apoiando Monica

Respostas:

185

Se você está procurando por modificação de Hash, sua solução funciona bem. No entanto, se você quiser alterar a consulta, pode usar o pushState, como você disse. Aqui está um exemplo que pode ajudá-lo a implementá-lo corretamente. Eu testei e funcionou bem:

if (history.pushState) {
    var newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?myNewUrlQuery=1';
    window.history.pushState({path:newurl},'',newurl);
}

Não recarrega a página, mas apenas permite que você altere a consulta de URL. Você não seria capaz de alterar o protocolo ou os valores do host. E, claro, requer navegadores modernos que possam processar a API HTML5 History.

Para maiores informações:

http://diveintohtml5.info/history.html

https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history

Fabio Nolasco
fonte
6
Eu acredito que você pode encurtar window.location.protocol + '//' + window.location.hostpara apenas: window.location.origin.
Garrett de
4
Além disso, para um pouco de informação adicional, caminhos relativos também funcionam history.pushState(). Nenhum stateargumento real é necessário. Ambos significam que você pode fazer algo simples, como history.pushState(null, '', '/foo?bar=true')se sua nova url estiver no mesmo host / porta.
Blaskovicz
23
Observe que, se você não quiser que o novo estado apareça no histórico do navegador, você pode usar history.replaceState()com os mesmos argumentos.
Blackus
6
USE history.replaceState () do contrário, você precisará clicar no botão VOLTAR duas vezes em seu navegador
Zhenya
4
em navegadores modernos, você pode simplesmente fazer:if (window.history.pushState) { const newURL = new URL(window.location.href); newURL.search = '?myNewUrlQuery=1'; window.history.pushState({ path: newURL.href }, '', newURL.href); }
Allan Baptista
8

Partindo da resposta de Fabio, criei duas funções que provavelmente serão úteis para quem tiver essa pergunta. Com essas duas funções, você pode chamar insertParam()com uma chave e um valor como argumento. Ele adicionará o parâmetro de URL ou, se já existir um parâmetro de consulta com a mesma chave, mudará esse parâmetro para o novo valor:

//function to remove query params from a URL
function removeURLParameter(url, parameter) {
    //better to use l.search if you have a location/link object
    var urlparts= url.split('?');   
    if (urlparts.length>=2) {

        var prefix= encodeURIComponent(parameter)+'=';
        var pars= urlparts[1].split(/[&;]/g);

        //reverse iteration as may be destructive
        for (var i= pars.length; i-- > 0;) {    
            //idiom for string.startsWith
            if (pars[i].lastIndexOf(prefix, 0) !== -1) {  
                pars.splice(i, 1);
            }
        }

        url= urlparts[0] + (pars.length > 0 ? '?' + pars.join('&') : "");
        return url;
    } else {
        return url;
    }
}

//function to add/update query params
function insertParam(key, value) {
    if (history.pushState) {
        // var newurl = window.location.protocol + "//" + window.location.host + search.pathname + '?myNewUrlQuery=1';
        var currentUrlWithOutHash = window.location.origin + window.location.pathname + window.location.search;
        var hash = window.location.hash
        //remove any param for the same key
        var currentUrlWithOutHash = removeURLParameter(currentUrlWithOutHash, key);

        //figure out if we need to add the param with a ? or a &
        var queryStart;
        if(currentUrlWithOutHash.indexOf('?') !== -1){
            queryStart = '&';
        } else {
            queryStart = '?';
        }

        var newurl = currentUrlWithOutHash + queryStart + key + '=' + value + hash
        window.history.pushState({path:newurl},'',newurl);
    }
}
jmona789
fonte
como suas funções são melhores do que new URLSearchParams()com seus métodos nativos como definir e excluir ? em ambos os casos, devemos colocá-lo para a história comhistory.pushState()
AlexNikonov
1
new URLSearchParams()não é compatível com nenhuma versão do IE
jmona789
Obrigado @ jmona789 isso funcionou perfeitamente para mim e era exatamente o que eu estava procurando
mknopf
7

Usei a seguinte biblioteca JavaScript com grande sucesso:

https://github.com/balupton/jquery-history

Ele suporta a API de histórico HTML5, bem como um método de fallback (usando #) para navegadores mais antigos.

Esta biblioteca é essencialmente um polyfill em torno de `history.pushState '.

Ian Newson
fonte
impressionante! você testou com todos os navegadores ??
Sonic Soul de
Minha implementação foi testada no IE7 +, Firefox 3.6+, Safari 5 e Chrome 16+. Provavelmente funciona com outros navegadores também, mas não tive nenhuma reclamação nos vários sistemas implantados com ele.
Ian Newson
ótimo. então agora .. simplesmente colocar um # em vez de & ao escrever em window.location.href funciona para mim. na medida em que não recarrega a página. tenho certeza que ele vai quebrar assim que eu testá-lo no IE. Nesse ponto, irei com a biblioteca que você sugeriu. obrigado
Sonic Soul de
O método # é bom porque tem um amplo suporte a navegadores. As coisas podem ficar complicadas quando você precisa incluir essas informações nas solicitações ao servidor, já que a parte # da URL não é enviada pelo navegador. Existem maneiras de contornar isso, incluindo a biblioteca que mencionei. Além disso, usar a API de histórico do HTML5 ajuda a tornar seus URLs mais curtos em geral e requer menos trabalho do lado do cliente para restaurar o estado.
Ian Newson
O # método também é problemático para compartilhamento de mídia social; Twitter, Facebook e possivelmente outros não funcionam bem com tags âncora ao criar visualizações ou links para o compartilhamento.
mirth23,
5

Eu quero melhorar a resposta do Fabio e criar uma função que adiciona uma chave personalizada à string do URL sem recarregar a página.

function insertUrlParam(key, value) {
    if (history.pushState) {
        let searchParams = new URLSearchParams(window.location.search);
        searchParams.set(key, value);
        let newurl = window.location.protocol + "//" + window.location.host + window.location.pathname + '?' + searchParams.toString();
        window.history.pushState({path: newurl}, '', newurl);
    }
}
Aram Hovhannisyan
fonte
0

Então, a API de histórico é exatamente o que você está procurando. Se você deseja oferecer suporte a navegadores legados também, procure uma biblioteca que use a manipulação da hashtag da URL se o navegador não fornecer a API de histórico.

chucktator
fonte