2012-06-26 11 views
27

मैंने कई संबंधित धागे के माध्यम से पढ़ा है लेकिन उनमें से कोई भी समाधान प्रदान नहीं करता है।Backbone.js एप्लिकेशन में हैशचेंज पर स्क्रॉल स्थिति को कैसे संभालें?

जो मैं करने की कोशिश कर रहा हूं वह है कि मेरे बैकबोन.जेएस ऐप में बुद्धिमानी से स्क्रॉलबार को संभाल लें। कई अन्य लोगों की तरह, मेरे पास #mypage हैश मार्ग हैं। इनमें से कुछ मार्ग पदानुक्रमित हैं। जैसे मेरे पास एक #list पृष्ठ है जो कुछ आइटम सूचीबद्ध करता है, मैं सूची में किसी आइटम पर क्लिक करता हूं। फिर यह # व्यू/ITEMID पृष्ठ खोलता है।

मेरे पृष्ठ सभी HTML लेआउट में एक ही सामग्री div साझा करते हैं। एक नेविगेशन परिवर्तन पर, मैं सामग्री div में उस मार्ग के दृश्य का प्रतिनिधित्व करने वाला एक नया div इंजेक्ट करता हूं, जो पहले वहां था उसे बदल देता था।

तो अब मेरी समस्या:

तो आइटम दूर सूची में नीचे है मैं वहाँ पाने के लिए स्क्रॉल करने के लिए हो सकता है। जब मैं उस पर क्लिक करता हूं, तो "डिफ़ॉल्ट" बैकबोन व्यवहार यह है कि # व्यू/ITEMID पृष्ठ उसी स्क्रॉल स्थिति पर प्रदर्शित होता है जो #list दृश्य था। फिक्सिंग इतना आसान है; जब भी कोई नया दृश्य इंजेक्शन दिया जाता है तो बस $ (दस्तावेज़) .scrollTop (0) जोड़ें।

समस्या यह है कि अगर मैं बैक बटन दबाता हूं तो मैं स्क्रॉल स्थिति पर #list दृश्य पर वापस जाना चाहता हूं।

मैंने इस पर स्पष्ट समाधान लेने की कोशिश की। स्मृति में पदों को स्क्रॉल करने के लिए मार्गों का नक्शा संग्रहित करना। मैं हैंडलर घटना के लिए हैंडलर की शुरुआत में इस मानचित्र को लिखता हूं, लेकिन नए दृश्य को वास्तव में डोम में डाल दिया जाता है। डीओएम में नया दृश्य होने के बाद, मैंने हैशचेंज हैंडलर के अंत में मानचित्र से पढ़ा।

जो मैं देख रहा हूं वह यह है कि फ़ायरफ़ॉक्स में कम से कम, कहीं भी, हैशचेंज ईवेंट के हिस्से के रूप में पृष्ठ को स्क्रॉल कर रहा है, ताकि जब तक मेरा लिखने-टू-मैप कोड कॉल हो जाए, दस्तावेज़ में एक जीता स्क्रॉल स्थिति जो निश्चित रूप से उपयोगकर्ता द्वारा स्पष्ट रूप से नहीं बनाई गई थी।

कोई भी यह जानता है कि इसे कैसे ठीक किया जाए, या एक सर्वोत्तम अभ्यास जिसे मैं इसके बजाय उपयोग कर रहा हूं?

मैंने दो बार चेक किया है और मेरे डोम में कोई एंकर टैग नहीं है जो मैं उपयोग कर रहे हैंश से मेल खाता हूं।

+0

आप 'स्थिति() शीर्ष' का उपयोग करके मौके से नहीं हैं और आपके पास तत्वों को प्राप्त करने वाले माता-पिता तत्व हैं जो डोम लोड के बाद 'कुछ भी' हैं? – Ohgodwhy

+0

नहीं, मैं स्क्रॉल स्थिति – schematic

उत्तर

13

इसका मेरा समाधान समाप्त होने से कुछ कम स्वचालित हो गया, लेकिन कम से कम यह लगातार है।

यह सहेजने और बहाल करने के लिए मेरा कोड था। यह कोड मेरे वास्तविक समाधान पर मेरे प्रयास से काफी अधिक था, जिसे इसे विभिन्न घटनाओं पर बुलाया गया था। "मुलायम" एक ध्वज है कि यह एक ब्राउज़र कार्रवाई (बैक, फॉरवर्ड, या हैश क्लिक) से आया है, जो राउटर.नाविगेट() को "हार्ड" कॉल के विपरीत है। एक नेविगेट() कॉल के दौरान मैं बस शीर्ष पर स्क्रॉल करना चाहता था।

restoreScrollPosition: function(route, soft) { 
    var pos = 0; 
    if (soft) { 
     if (this.routesToScrollPositions[route]) { 
      pos = this.routesToScrollPositions[route]; 
     } 
    } 
    else { 
     delete this.routesToScrollPositions[route]; 
    } 
    $(window).scrollTop(pos); 
}, 

saveScrollPosition: function(route) { 
    var pos = $(window).scrollTop(); 
    this.routesToScrollPositions[route] = pos; 
} 

मैं भी Backbone.History संशोधित ताकि हम (जो checkUrl कॉल) एक "सॉफ्ट" इतिहास परिवर्तन पर प्रतिक्रिया प्रोग्राम के रूप में एक "हार्ड" इतिहास परिवर्तन ट्रिगर बनाम के बीच अंतर बता सकते हैं। यह इस ध्वज को राउटर कॉलबैक में भेजता है।

_.extend(Backbone.History.prototype, { 

    // react to a back/forward button, or an href click. a "soft" route 
    checkUrl: function(e) { 
     var current = this.getFragment(); 
     if (current == this.fragment && this.iframe) 
      current = this.getFragment(this.getHash(this.iframe)); 
     if (current == this.fragment) return false; 
     if (this.iframe) this.navigate(current); 
     // CHANGE: tell loadUrl this is a soft route 
     this.loadUrl(undefined, true) || this.loadUrl(this.getHash(), true); 
    }, 

    // this is called in the whether a soft route or a hard Router.navigate call 
    loadUrl: function(fragmentOverride, soft) { 
     var fragment = this.fragment = this.getFragment(fragmentOverride); 
     var matched = _.any(this.handlers, function(handler) { 
      if (handler.route.test(fragment)) { 
       // CHANGE: tell Router if this was a soft route 
       handler.callback(fragment, soft); 
       return true; 
      } 
     }); 
     return matched; 
    }, 
}); 

मूल रूप से मैं स्क्रॉल बचत करने और पूरी तरह से हैशनल हैंडलर के दौरान बहाल करने की कोशिश कर रहा था। अधिक विशेष रूप से, राउटर के कॉलबैक रैपर के भीतर, अज्ञात फ़ंक्शन जो आपके वास्तविक मार्ग हैंडलर को आमंत्रित करता है।

route: function(route, name, callback) { 
    Backbone.history || (Backbone.history = new Backbone.History); 
    if (!_.isRegExp(route)) route = this._routeToRegExp(route); 
    if (!callback) callback = this[name]; 
    Backbone.history.route(route, _.bind(function(fragment, soft) { 

     // CHANGE: save scroll position of old route prior to invoking callback 
     // & changing DOM 
     displayManager.saveScrollPosition(foo.lastRoute); 

     var args = this._extractParameters(route, fragment); 
     callback && callback.apply(this, args); 
     this.trigger.apply(this, ['route:' + name].concat(args)); 

     // CHANGE: restore scroll position of current route after DOM was changed 
     // in callback 
     displayManager.restoreScrollPosition(fragment, soft); 
     foo.lastRoute = fragment; 

     Backbone.history.trigger('route', this, name, args); 
    }, this)); 
    return this; 
}, 

मैं चीजों को इस तरह संभाल करना चाहता था, क्योंकि यह सभी मामलों में बचत की अनुमति देता है कि क्या एक href क्लिक करें, वापस बटन, आगे बटन, या नेविगेट() कॉल।

ब्राउज़र में एक "सुविधा" है जो हैशचेंज पर आपकी स्क्रॉल को याद रखने की कोशिश करती है, और हैश पर वापस जाने पर इसे स्थानांतरित करती है। आम तौर पर यह बहुत अच्छा होता, और मुझे इसे लागू करने की सभी परेशानी बचाता। समस्या यह है कि मेरा ऐप, कई लोगों की तरह, पृष्ठ से पृष्ठ पर डीओएम की ऊंचाई को बदलता है।

उदाहरण के लिए, मैं एक लंबा #list दृश्य पर हूं और नीचे स्क्रॉल किया है, फिर एक आइटम पर क्लिक करें और एक छोटी सी #detail व्यू पर जाएं जिसमें कोई स्क्रॉलबार नहीं है। जब मैं बैक बटन दबाता हूं, तो ब्राउज़र मुझे #list दृश्य के लिए अंतिम स्थिति तक स्क्रॉल करने का प्रयास करेगा। लेकिन दस्तावेज़ अभी तक लंबा नहीं है, इसलिए ऐसा करने में असमर्थ है। जब तक #list के लिए मेरा मार्ग बुलाया जाता है और मैं सूची को फिर से दिखाता हूं, स्क्रॉल स्थिति खो जाती है।

तो, ब्राउज़र की अंतर्निहित स्क्रॉल मेमोरी का उपयोग नहीं कर सका। जब तक मैंने दस्तावेज़ को एक निश्चित ऊंचाई नहीं बनाया या कुछ डोम चालबाजी नहीं की, जिसे मैं नहीं करना चाहता था।

इसके अलावा अंतर्निहित स्क्रॉल व्यवहार उपर्युक्त प्रयास को गड़बड़ कर देता है, क्योंकि सहेजने के लिए कॉल बहुत देर हो चुकी है - ब्राउजर ने तब तक स्क्रॉल स्थिति बदल दी है।

इसका समाधान, जो स्पष्ट होना चाहिए था, मार्ग कॉलबैक रैपर के बजाय राउटर.नाविगेट() से saveScrollPosition को कॉल कर रहा था। यह गारंटी देता है कि ब्राउजर हैशचेंज पर कुछ भी करने से पहले मैं स्क्रॉल स्थिति को सहेज रहा हूं।

route: function(route, name, callback) { 
    Backbone.history || (Backbone.history = new Backbone.History); 
    if (!_.isRegExp(route)) route = this._routeToRegExp(route); 
    if (!callback) callback = this[name]; 
    Backbone.history.route(route, _.bind(function(fragment, soft) { 

     // CHANGE: don't saveScrollPosition at this point, it's too late. 

     var args = this._extractParameters(route, fragment); 
     callback && callback.apply(this, args); 
     this.trigger.apply(this, ['route:' + name].concat(args)); 

     // CHANGE: restore scroll position of current route after DOM was changed 
     // in callback 
     displayManager.restoreScrollPosition(fragment, soft); 
     foo.lastRoute = fragment; 

     Backbone.history.trigger('route', this, name, args); 
    }, this)); 
    return this; 
}, 

navigate: function(route, options) { 
    // CHANGE: save scroll position prior to triggering hash change 
    nationalcity.displayManager.saveScrollPosition(foo.lastRoute); 
    Backbone.Router.prototype.navigate.call(this, route, options); 
}, 

दुर्भाग्य से यह भी मतलब है मैं हमेशा स्पष्ट रूप से, नेविगेट कॉल करने के लिए() अगर मैं स्क्रॉल स्थिति की बचत करने में रुचि है के रूप में बस अपना टेम्पलेट्स में href = "# myhash" का उपयोग करने का विरोध किया है।

ओह ठीक है। यह काम करता हैं। :-)

+0

दोनों को पढ़ने और लिखने के लिए $ (दस्तावेज़) .scrollTop() का उपयोग कर रहा था, बस क्रेडिट देना चाहते हैं कि उपर्युक्त ओवरराइडिंग कोड मूल बैकबोन कोड से लिया गया था, क्योंकि मुझे छोटे बदलाव करने की आवश्यकता थी उस मौजूदा कोड के लिए जो अधिक ओओपी-आईएसएच तरीके से नहीं किया जा सका। – schematic

0

मेरे पास इसके लिए थोड़ा गरीब-आदमी का फिक्स है। मेरे ऐप में, मुझे एक ही समस्या थी। मैं के साथ एक कंटेनर में सूची दृश्य और आइटम दृश्य डाल कर इसे हल: जब मैं पर क्लिक करें

overflow-y: auto 
height: 100% 

तब:

height: 100% 

तो मैं करने के लिए दोनों सूची दृश्य और आइटम दृश्य सेट एक आइटम, मैं सूची छुपाता हूं और आइटम दृश्य दिखाता हूं। इस तरह जब मैं आइटम बंद करता हूं और सूची में वापस जाता हूं, तो मैं अपनी जगह सूची में रखता हूं। यह एक बार बैक बटन के साथ काम करता है, हालांकि जाहिर है कि यह आपके इतिहास को नहीं रखता है, इसलिए एकाधिक बैक बटन क्लिक आपको प्राप्त नहीं करेंगे जहां आपको होना चाहिए। फिर भी, कोई जे एस के साथ एक समाधान है, इसलिए यदि यह काफी अच्छा है ...

5

एक सरल समाधान: स्टोर एक चर में हर पुस्तक घटना पर सूची दृश्य की स्थिति:

var pos; 
$(window).scroll(function() { 
    pos = window.pageYOffset; 
}); 

से लौट रहे जब आइटम दृश्य, संग्रहीत स्थिति में सूची दृश्य को स्क्रॉल करें:

window.scrollTo(0, pos); 
+0

हाँ, यह काम करता है, और मुझे याद दिलाता है कि हम पृष्ठ के शीर्ष पर मैन्युअल रूप से सुनिश्चित करने के लिए किसी भी शो शो ईवेंट (मैरियनेट के माध्यम से) को स्क्रॉल संलग्न कर सकते हैं। – MBHNYC

0

@ Mirage114 आपका समाधान पोस्ट करने के लिए धन्यवाद। यह एक सम्मोहन की तरह काम करता है। बस एक मामूली बात, यह मानता है कि मार्ग कार्य तुल्यकालिक हैं। यदि कोई एसिंक ऑपरेशन है, उदाहरण के लिए दृश्य प्रस्तुत करने से पहले दूरस्थ डेटा प्राप्त करना, तो दृश्य सामग्री को DOM में जोड़ने से पहले विंडो स्क्रॉल हो जाती है।मेरे मामले में, जब पहली बार मार्ग का दौरा किया जाता है तो मैं डेटा कैश करता हूं। ऐसा इसलिए होता है कि जब कोई उपयोगकर्ता ब्राउज़र बैक/फॉरवर्ड बटन हिट करता है, तो डेटा लाने के एसिंक ऑपरेशन से बचा जाता है। हालांकि मार्ग के लिए आवश्यक प्रत्येक डेटा को कैश करना हमेशा संभव नहीं हो सकता है।

संबंधित मुद्दे