2009-11-14 12 views
26

ho creato una demo: http://pastebin.me/584b9a86d715c9ba85b7bebf0375e237jQuery elenco ordinabile - barra di scorrimento salta durante l'ordinamento

Quando la barra di scorrimento è in basso e si trascinano gli elementi di ordinarli, provoca la barra di scorrimento per saltare. Sembra farlo in FF, Safari, Chrome e IE (almeno IE8).

In Firefox sul mio Mac, fa il salto quando la barra di scorrimento è in fondo, ma aggiunge anche un bel flash all'intera lista. Lampeggia l'intero elenco proprio mentre lo scorre. Credo che se capisco che cosa sta causando la pergamena (e se riesco a fermarlo), anche il lampeggio si fermerà.

Non mi piace che salti in quel modo b/c lo trovo sconcertante e confuso. So che questo è un caso angoscioso, ma mi piacerebbe aggiustarlo se potessi.

Quindi, esiste un modo per impedirne lo scorrimento? In alternativa, che cosa sta causando di scorrere verso l'alto?

Grazie

+0

Sono in FF 3.6 e non vedo alcun problema reale. Se afferro quello in basso e lo trascino verso l'alto, la scatola non scorre finché non raggiunge la cima. – Bialecki

+0

Prova [questa soluzione] (http://stackoverflow.com/a/14361849/5444623) prima di scherzare con javascript – PeterM

risposta

45

Il problema è molto semplice.

Per prima cosa impostiamo lo scenario. Hai una lista di elementi ordinabili, la tua pagina è più in alto del tuo schermo quindi ha una barra di scorrimento, la tua pagina è in fondo, la barra di scorrimento fino in fondo.

Il problema, si va a trascinare un elemento che causa jQuery per rimuoverlo dalla dom, quindi aggiungere un segnaposto. Rallentiamolo, rimuove prima l'elemento, ora l'elenco è più breve e la pagina si accorcia, causando una riduzione della barra di scorrimento. Quindi aggiunge il segnaposto, ora la pagina è più lunga, ora la barra di registrazione è più lunga (ma la finestra non scorre indietro, quindi sembra che la barra di scorrimento sia saltata in alto).

Il modo migliore che ho trovato per contrastare questo sarebbe garantire che l'elenco ordinabile abbia un'altezza statica, quindi la rimozione di un elemento non lo fa andare più corto. Un metodo per fare questo sarebbe

create:function(){ 
    jQuery(this).height(jQuery(this).height()); 
} 

È possibile che questo sarà chiamato al momento della creazione della lista ordinabile, staticly impostando la sua altezza alla sua altezza attuale.

Se si dispone di immagini in uno degli elementi ordinabili, il codice sopra riportato non funzionerà se non si predefiniscono le dimensioni nel tag. Il motivo è che quando l'altezza() viene chiamata e impostata, sta calcolando prima che l'immagine venga riempita. Nessuna dimensione, quindi l'immagine non tiene conto dello spazio. Una volta caricata l'immagine, occuperà spazio.

Ecco un modo per definire le dimensioni. Ridimensiona semplicemente l'altezza della lista ogni volta che viene rilevata un'immagine da caricare.

create:function(){ 
    var list=this; 
    jQuery(list).find('img').load(function(){ 
     jQuery(list).height(jQuery(list).height()); 
    }); 
} 

Versione finale:

jQuery(document).ready(function(){ 
    jQuery("div#todoList").sortable({ 
     handle:'img.moveHandle', 
     opacity: 0.6, 
     cursor: 'move', 
     tolerance: 'pointer', 
     forcePlaceholderSize: true, 
     update:function(){ 
      jQuery("body").css('cursor','progress'); 
      var order = jQuery(this).sortable('serialize'); 
      jQuery.post("/ajax.php?do=sort&"+order,function(data){jQuery("body").css('cursor','default');}); 
     }, 
     create:function(){ 
      var list=this; 
      resize=function(){ 
       jQuery(list).css("height","auto"); 
       jQuery(list).height(jQuery(list).height()); 
      }; 
      jQuery(list).height(jQuery(list).height()); 
      jQuery(list).find('img').load(resize).error(resize); 
     } 
    }); 
}); 
+1

Questo codice ha risolto anche il mio problema. Grazie. ma c'è un altro problema da questa correzione. Nel caso in cui l'elenco ordinabile abbia l'opzione Rimuovi riga. Una volta che una singola riga è stata rimossa dall'elenco, c'è spazio lasciato lì perché l'altezza della lista è fissa. – vee

+1

e un altro problema. Questo non può essere risolto se l'elenco ordinabile non è visibile. ex: elenco ordinabile è in schede non attive. – vee

+0

Lo stesso qui, assicurati di ridimensionare anche quando la finestra viene ridimensionata, in particolare per i siti Web reattivi. Perché l'altezza del contenuto potrebbe cambiare. Ma +1 per gestire il caso dell'immagine. – RaphKomjat

13

ho sperimentato questo problema pure. Succede quando la lista determina la lunghezza della tua pagina. Quando inizi a ordinare, l'altezza dell'elenco cambia per una frazione di secondo, facendo saltare la pagina. Ecco una soluzione strana, ma molto funzionale a questo problema:

$('ul').height($('ul').height()); 
+1

+1 Facilmente la soluzione migliore qui. –

+0

concordato. Il motivo per cui il problema sta accadendo è spiegato chiaramente, la correzione funziona perfettamente, è veloce e semplice. –

+0

+1 per semplicità. – fedxc

2

Grazie per la grande soluzione ma mi consiglia di utilizzare il seguente comando:

create:function(){ 
    var list=this; 
    resize=function(){ 
     jQuery(list).css("min-height","0"); 
     jQuery(list).height(jQuery(list).height()); 
    }; 
    jQuery(list).css('min-height', jQuery(list).height()); 
} 
+0

Un po 'in ritardo rispetto al gioco per i commenti, ma preferisco anche questo metodo ... specialmente quando aggiungi elementi ordinabili al volo. +1 –

+3

cos'è questo? il ridimensionamento non è usato affatto –

15

Ho avuto lo stesso problema quando si utilizza un contenitore div e div gli elementi e l'impostazione dell'altezza del contenitore non mi sono stati d'aiuto, ma impostando esplicitamente l'overflow: la proprietà auto css sul div contenente risolveva il problema. Solo testato in Chrome però.

+2

"Sei il mio salvatore, amico!" Funziona semplicemente brillante. (Funziona anche in Firefox) – antongorodezkiy

+1

Strano, ma funziona! Grazie! – JohnA10

+0

Aggiungo "overflow: auto;" al mio contenitore "ol" e funziona. Grazie !! – drinor

0

Ho riscontrato questo problema in Chrome. E tutte le divs ordinabili avevano altezza fissa.

Il problema era nella proprietà css position: relative di questi div. Quando il div è stato trascinato e rimosso dal DOM, le sue coordinate sono state calcolate errate a causa della posizione relativa.

Inherit o absolute il posizionamento deve essere utilizzato per elementi ordinabili.

5

Lol, sembra che questo non sia stato riparato 4 anni dopo. Ecco lo ticket.

Aggiungendo alla risposta di @Carl M. Gregory, per sfruttare pienamente un contenitore dinamico invece di impostarlo su un'altezza fissa, modifico il file core jquery.ui.sortable.js.

Basta spostare di nuovo la linea 228. this._createPlaceHolder... prima linea 208. this.helper.css("position", "absolute");

Grazie alla Honda sul portale biglietto di jQuery. Sto usando Sortable.js versione 1.10.3.

+2

Se qualcuno usa un'altra versione, o anche una compilazione personalizzata dell'interfaccia utente jQuery (con numeri di linea diversi), cerca il codice del widget Sortable e all'interno della funzione _mouseStart() sposta la riga 'this._createPlaceHolder ...' prima della riga ' this.helper.css ("position", "absolute"); '. –

+0

Bella correzione grazie, risposta aggiornata. –

+0

Hmm ... che fa ancora un piccolo salto anche se non così alto come prima. – flu

0

Ecco come risolvo il problema.

$(".groups").sortable({ 
    ... 
    helper: function(event, ui){ 
     lastScrollPosition = $("body").scrollTop(); 
    }, 
    start: function(e, ui){ 
     // avoid scroll jump 
     $("body").scrollTop(lastScrollPosition); 
    }, 
    }); 
1

Sembra che jQuery UI 1.9.2 abbia risolto il problema.

Se non è possibile modificare la libreria, è disponibile una soluzione che include un'operazione di scorrimento semplice. L'idea è semplice:

  • Mantieni la posizione di scorrimento precedente ogni volta che scorri.
  • Consente di tornare alla posizione precedente quando si avvia il trascinamento dell'elemento.

Qui vai;

var lastScrollPosition = 0; //variables defined in upper scope 
var tempScrollPosition = 0; 

window.onscroll = function() { // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll 
    clearTimeout($.data(this, 'scrollTimer')); 
    $.data(this, 'scrollTimer', setTimeout(function() { 
     tempScrollPosition = lastScrollPosition; // Scrolls don't change from position a to b. They cover some numbers between a and b during the change to create a smooth sliding visual. That's why we pick the previous scroll position with a timer of 250ms. 
    }, 250)); 

    lastScrollPosition = $(document).scrollTop(); // Up to you requirement you may change it to another elemnet e.g $("#YourPanel").onscroll 
}; 

$("#YourSortablePanel").sortable({ 
    start: function (event, ui) { 
     $(document).scrollTop(tempScrollPosition); 
    } 
}); 
+1

Questo mi sembra un po 'un attacco dato che devo impostare una variabile semi-globale, ma è l'unica cosa che ha funzionato per me su questo thread. Sto usando un div a scorrimento laterale in cui il contenuto è nascosto, quindi la maggior parte delle altre soluzioni non si applicano a me. Sto anche usando il rails-ui gem, quindi non posso modificare il file principale. Grazie! – CHawk

+0

Ho avuto condizioni simili con te e mi è venuta in mente questa idea. Ricordo di aver guardato attraverso tonnellate di soluzioni senza fortuna per giorni. Sono molto contento che questo abbia salvato il tempo di qualcun altro. –

1

Carl M. Gregory sta spiegando il "perché questo sta accadendo" molto chiaramente.

Tuttavia, il fissaggio dell'altezza non sembra sufficiente, poiché il contenuto interno può diventare più alto quando si ridimensiona la finestra.

Quello che desidero si poteva fare:

  1. Ascolta ordinabile evento 'start', e fissare l'altezza del ordinabile contenitore
  2. Ascolta ordinabile evento 'stop', e arretrato l'altezza su auto (per assicurarsi che non v'è alcun problema quando si ridimensiona la finestra)

Cosa succede in pratica: il salto avviene BEF ORA prendiamo l'evento di inizio, quindi questo non può essere utilizzato

La soluzione: ascoltare gli eventi di mouse e mouse jquery sugli elementi ordinabili. È molto semplice.

$(".sortable_item").mousedown(function(){ 
    $(".sortable_container").height($(".sortable_container").height()); 
}).mouseup(function(){ 
    $(".sortable_container").height('auto'); 
}); 

Suggerisco di considerare ancora il punto di Carl sul caricamento delle immagini. La sua risposta merita una lettura.

3

Carl M. Gregory spiega perfettamente la questione, ma penso che la sua soluzione sia inutilmente complicata.

L'impostazione dell'elenco altezza su valore fisso risolve il problema, ma farlo al momento della creazione dell'elenco è troppo presto. È possibile utilizzare la funzione .mousedown() per impostare l'altezza prima dell'inizio dell'ordinamento. A questo punto qualsiasi potenziale immagine è già caricata, quindi non è necessario controllarne le dimensioni.

Quindi il salto non si verifica, ma ora si prega di considerare questo: si dispone di più elenco collegato e sono disposti verticalmente. Ora, questo accade:

  1. si inizia a trascinare dalla lista superiore
  2. La lista in alto la società ha fissato altezza
  3. Spostare l'elemento all'elenco fondo
  4. lista di fondo ben si adatta, perché la sua altezza è auto
  5. la lista superiore ha ancora la stessa altezza fissa se dovrebbe essere un elemento più breve

Quindi, ad un certo punto, si dovrebbe riportare l'altezza a auto. Quando? Il mouseup è troppo tardi (vedi https://jsfiddle.net/937vv4tx/1/), ma va bene, è possibile restituire il valore corretto quasi immediatamente dopo il salto non è più una minaccia. Utilizzando l'evento start()

Aggiornamento: Ma ancora voglia di impostare l'altezza a auto su .mouseup() perché se è sufficiente fare clic sulla voce e non si inizia a trascinare, imposta ancora altezza fissa perché il .mousedown() è già accaduto.

$('.sortable').mousedown(function() { 
    // set fixed height to prevent scroll jump 
    // when dragging from bottom 
    $(this).height($(this).height()); 
}).mouseup(function() { 
    // set height back to auto 
    // when user just clicked on item 
    $(this).height('auto'); 
}).sortable({ 
    connectWith: '.sortable', 
    placeholder: 'placeholder', 
    start: function() { 
     // dragging is happening 
     // and scroll jump was prevented, 
     // we can set it back to auto 
     $(this).height('auto'); 
    } 
}); 

Questo codice funziona perfettamente per me.

0

Stavo caricando i miei sortable divs VIA Ajax in un wrapper con un'altezza automatica.

Anziché tentare di collegarsi a eventi ordinabili, ho appena realizzato l'altezza automatica durante il caricamento e la modifica dell'elenco e successivamente l'ho impostato su un'altezza statica in seguito.

La soluzione di Vit è simile, ma non ho voluto aggiungere costi aggiuntivi inutili facendo questo su ogni evento mousedown e mouseup. Invece, lo faccio solo quando la lista si sta modificando sul carico o quando vengono aggiunti nuovi elementi (io uso un'animazione slideDown e appena l'altezza è statica quando è completa).

$("#wrapper").load("list.php",function() { 
    $("#wrapper").css({ "height":"auto" }); 
    $("#wrapper").css({ "height": $("#wrapper").height() }); 
} 

Le altre soluzioni non funzionavano per me quando ho ricaricare i dati come ho solo fatto loro ordinabili una volta durante il caricamento della pagina.

La mia lista/div cambia con un menu a discesa quindi sono in grado di selezionare elenchi diversi e il mio wrapper si ridimensiona di conseguenza senza il salto fastidioso.

Se è necessario aggiungere elementi o ricaricare nulla, è sufficiente lanciare queste due righe di codice in una funzione e chiamare la funzione.

Spero che questo aiuti qualcuno!

2

Quindi, complesso! Tutto quello che devi fare è aggiungere overflow: auto; al genitore (ul, #element, whatever_you_call_it), e dovrebbe funzionare. Ciò garantisce che anche se il documento cambia leggermente l'altezza, che durante il ridimensionamento, l'overflow automatico manterrà l'altezza dell'elemento.

Problemi correlati