We have a website that is running jQuery Infinite Scroll Plugin. The plugin is no longer maintained but it is the only one that really serves our purpose properly. However, the problem I have is that our site is ajax based. On page change the pg-changed trigger is fired against the window, which allows us to check if the Infinite Scroll container is there and enable Infinite Scroll. If the Infinite Scroll container isn't there but $.infscr exists, we will attempt to destroy the previous instance.
The problem I am having is that when changing to another page, it doesn't seem to be getting destroyed properly and sometimes AJAX calls will be made, along with the infscr loading bar displaying. Here is the code I am using to instantiate and destroy the plugin:
$(window).on('pg-changed', function () {
// delete our infinite scroll
if(typeof $.infscr !== 'undefined') {
$('.snap-inner, .infscr').data('infinitescroll', null);
$('.snap-inner, .infscr').infinitescroll('unbind');
$('.snap-inner, .infscr').infinitescroll('destroy');
$('#infscr-loading').remove();
$.infscr.data('infinitescroll', null);
$.infscr.infinitescroll('unbind');
$.infscr.infinitescroll('destroy');
delete $.infscr;
}
// setup our infinite scroll
if($('.infscr').length) {
$.infscr = $('.infscr').infinitescroll({
// define our navigation selectors
navSelector : 'div.infscr-navigation',
nextSelector : 'div.infscr-navigation A:first',
itemSelector : '.infscr-item',
// allow scrolling an overflowed element
behavior : 'local',
bufferPx : 120,
binder : $('.snap-inner'),
dataType : 'html',
loading : {
msg : null,
selector : '.snap-content',
img : 'data:image/gif;base64,TRIMMED',
msgText : '<span class="infscr-loading">Loading...</span>',
}
}, function (arrayOfNewElems) {
// render background images on our new elements
$(this).renderBgImages();
});
}
});
I really hope you can help with this as it has become quite a problem now, firing on scroll, making AJAX calls and displaying the loading bar.
I guess the problem might be that the plugin binds events on elements external to itself. It's hard to say without further debugging (is the plugin deleted or does it fail to do so?). If it does, why would it fail to unbind the events? etc.
When you say, 'on another page', I suppose you are not loading a whole new page (which would 'reset' the javascript.
Though, depending on wether this could work for you, you might want to try unbinding ALL events on the document, and starting fresh.
$(document).add('*').off();
should do so. If it does, you could try to pinpoint what elements/events you need to unbind manually. Let me know if this works or not.
Related
hey guys i am very new to js and jquery in genenral and i was just going throught the plugin code of a gallery plugin , i can across the function called _loadevents , that had the following content , see below :
this.$navPrev.on('click.gallery', function (event) {
});
this.$navNext.on('click.gallery', function (event) {
});
this.$wrapper.on('webkitTransitionEnd.gallery transitionend.gallery OTransitionEnd.gallery', function (event) {
});
now $navPrev , $navNext , and $wrapper are obviously some HTML element , now my question is about another method i came across in the same plugin , look below :
destroy: function () {
// console.log('inside destroy');
this.$navPrev.off('.gallery');
this.$navNext.off('.gallery');
this.$wrapper.off('.gallery');
}
now i see that if this function is called all the event handlers will be taken off. now , can somebody tell me what is the necessacity of such a function , does it improve a plugins efficiency ? how or when does such a function get used and is it a common practice to write e destroy function for events in plugins ?
Thank you.
Alex-z .
Destroy functions in plugins enable a developer to reset or remove a plugin from an element, restoring the element to before the plugin was initialised. This is useful if, for example, you have a gallery plugin that works and looks fantastic on desktop, but you don't want it on mobile. You can listen to resize event on window and if the window size is smaller than e.g. 710px then destroy the plugin. This will remove all the added events, undo any DOM manipulation, and restore the html elements back to how they were before the plugin was first initialised (turn-wise, if the window size is larger than 710px then initialise the plugin).
They are generally considered good practice.
I'm paginating search results returned from an AJAX call with jScroll:
$('#search').keyup(function() {
var search = $(this).val();
$.get('/search', {search : search}, function(results) {
$('.scroll-table').html(results);
$('.scroll-table').jscroll();
});
});
After making a new search, when I scroll to the bottom, jScroll loads the content of the last href for the old search.
So if my old _nextHref was /search?query=A&page=3 and I enter B in the search field, instead of loading /search?query=B&page=2 from the new href, it will load /search?query=A&page=3 from the old href.
Apparently calling jscroll() from the ajax success function won't reconstruct it and _nextHref stays set to its old value. I tried destroying it before loading it, but it will keep it fom loading altogether:
$('#search').keyup(function() {
var search = $(this).val();
$('.scroll-table').jscroll.destroy();
$.get('/search', {search : search}, function(results) {
$('.scroll-table').html(results);
$('.scroll-table').jscroll(); /* now jScroll won't load at all */
});
});
Can you please give me an example how to reinitialize jScroll so it loads the new href?
I found a temporary solution by commenting out the following line:
// if (data && data.initialized) return;
This caused a further problem.. If the result list fits a single page (no pagination needed so there is no href on the first page, "Loading..." is displayed on the bottom of the list, because jScroll wanted to GET "/undefined" from the server. Here is how i fixed it:
// Initialization
if (_nextHref != 'undefined') {
$e.data('jscroll', $.extend({}, _data, {initialized: true, waiting: false, nextHref: _nextHref}));
_wrapInnerContent();
_preloadImage();
_setBindings();
} else {
_debug('warn', 'jScroll: nextSelector not found - destroying');
_destroy();
return false;
}
I don't know if there is a better way to do this, but now it works with AJAX calls as I expect it to work. If anyone knows of a proper way to reinitialize the plugin, please share it with us.
UPDATE: I created a proper fork of jScroll allowing it to be reinitialized on each AJAX load, preventing it from loading the old href, using:
$('.scroll').jscroll({
refresh: true
});
Hopefully this functionality gets merged in the main version.
If you don't want to patch jScroll, you can clear the jScroll data in your load (or get) callback:
var pane = $('#myInfiniteScroll');
pane.load(url, function() {
pane.data('jscroll', null);
pane.jscroll({
nextSelector: "link[rel='next']",
autoTrigger: true,
});
});
When you call the jscroll function, pass the same parameters as when you first initialized it (in this example, I defined two configuration parameters, but use what you need.). Better yet, factor that out into its own function so you don't end up duplicating code.
I'm using jQuery with the bxSlider plugin, here is the link to it just incase: http://bxslider.com/
I'm trying to reload the slider and my custom pager after I've removed certain slides from it.
Here is what I have tried:
$(function() {
var slider = $('#slider').bxSlider({
pagerCustom: '#bx-pager'
});
$('.list').on('click', '.delete', function() {
image = $(this).closest('li').find('[type="hidden"]');
// image.attr('id') contains a string: image-0, image-1, image-2, etc.
$('#slider, #bx-pager').find('.' + image.attr('id')).remove();
slider.reloadSlider({
pagerCustom: '#bx-pager'
}); // I have also tried: slider.reloadSlider();
});
});
It works partially. What happens is the slider gets reloaded just fine but it removes the pager completely when it runs the reload.
Thank you very much for any help.
As long as I see, this is a bug in bxSlider, in fact, when you call the reloadSlider method, internally are called the methods destroySlider and init.
In the destroySlider method the pagerEl element is destroyed, this is right if you are not using a custom one, because it is recreated programmatically in the init method, but if you are using a custom one it can't be recreated programmatically.
I ended up modifying the destroySlider method to check if a custom pager is used, in this case it must not be deleted.
Here is the before (line 1294):
if(slider.pagerEl) slider.pagerEl.remove();
And after:
if (slider.settings.pagerCustom === '') {
if(slider.pagerEl) slider.pagerEl.remove();
}
I'll post the bug on GitHub as soon as I have the time.
I'm building Windows 8 app in JavaScript. What I'm trying to do is to slide the html element out of the screen and then change its "display" property to "none":
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide();
});
But this code doesn't work correctly: it just immediately hides the element without animation.
I've also tried:
var panelContainer = $('#panelContainer');
panelContainer.animate({ right: '-400px' }, 200, function () {
panelContainer.hide(200);
});
and it works, but it's a hack: I don't want to change the opacity when animating and I don't need to have additional timeout for hiding.
I've found that jQuery UI library has extended show and hide methods that do that, but I would like not to reference this library just for one call. I'm aware that there is a WinJS.UI.Flyout that performs similar operation, but it's not applicable in my case. Any ideas how this can be done?
The problem was that jQuery does not put hide animation into its animation queue by default. That's why my initial code was hiding the html element first and then animating it. The solution for that is to call hide with the parameter that explicitly specifies that hide call should be queued:
panelContainer.hide({queue: true});
I'm using ExtJS 3.2.1 and I need a component almost identical to the bundled HtmlEditor, with one exception: it must start editing the HTML source code directly. The reason I don't use a normal TextArea is that the user should be able to preview the result of his actions before submitting.
I've tried calling toggleSourceEdit(), as per ExtJS documentation, with no success. Debugging, I see that the editor object has the sourceEditMode property set to true, and the Source Edit button seems as if it was "pressed", but clicking on it does not render the typed HTML, and clicking it again goes to the Source Mode.
I've tried calling toggleSourceEdit() after the container show() method, on the container afterLayout listener and on the editor afterRender listener. I've tried also calling it on another button that I added to the container. The result is the same on every try.
The only other option I see is updating ExtJS to 3.3.0, but I haven't seem anything related on the changelogs. Either way, it's going to be my next step. EDIT: The app had another problems when updating, we'll make a bigger effort to update later. As of right now, we are using the HtmlEditor in its original setting.
Thanks!
ran into the same problem (using 3.3.0 by the way)
stumbled upon a fix by dumb luck. i have no idea why this works, but second time is the charm. call it twice in a row to achieve the desired effect..
HTMLEditor.toggleSourceEdit(true);
HTMLEditor.toggleSourceEdit(true);
hope that helps!
Rather calling toggleSourceEdit(), try to setup the configuration while you create HtmlEditor Object
Using toggleSourceEdit() caused some problems for me. One was that this seemed to put the editor somewhere in limbo between source edit and WYSIWYG mode unless I used a timeout of 250ms or so. It also puts the focus in that editor, and I don't want to start the form's focus in the editor, especially since it's below the fold and the browser scrolls to the focused html editor when it opens.
The only thing that worked for me was to extend Ext.form.HtmlEditor and then overwrite toggleSourceEdit, removing the focus command. Then adding a listener for toggling to the source editor when the component is initialized. This is for Ext 4.1 and up. For older versions, replace me.updateLayout() with me.doComponentLayout().
var Namespace = {
SourceEditor: Ext.define('Namespace.SourceEditor', {
extend: 'Ext.form.HtmlEditor',
alias: 'widget.sourceeditor',
initComponent: function() {
this.callParent(arguments);
},
toggleSourceEdit: function (sourceEditMode) {
var me = this,
iframe = me.iframeEl,
textarea = me.textareaEl,
hiddenCls = Ext.baseCSSPrefix + 'hidden',
btn = me.getToolbar().getComponent('sourceedit');
if (!Ext.isBoolean(sourceEditMode)) {
sourceEditMode = !me.sourceEditMode;
}
me.sourceEditMode = sourceEditMode;
if (btn.pressed !== sourceEditMode) {
btn.toggle(sourceEditMode);
}
if (sourceEditMode) {
me.disableItems(true);
me.syncValue();
iframe.addCls(hiddenCls);
textarea.removeCls(hiddenCls);
textarea.dom.removeAttribute('tabindex');
//textarea.focus();
me.inputEl = textarea;
} else {
if (me.initialized) {
me.disableItems(me.readOnly);
}
me.pushValue();
iframe.removeCls(hiddenCls);
textarea.addCls(hiddenCls);
textarea.dom.setAttribute('tabindex', -1);
me.deferFocus();
me.inputEl = iframe;
}
me.fireEvent('editmodechange', me, sourceEditMode);
me.updateLayout();
}
})
}
Then to use it:
Ext.create('Namespace.SourceEditor', {
/*regular options*/
listeners: {
initialize: function(thisEditor) {
thisEditor.toggleSourceEdit();
}
}
});
htmlEditor.toggleSourceEdit(true);
one time should be enough if you do this listening to the afterrender event of the editor.