JQuery .sortable() only works AFTER page refresh - javascript

I have a case of a page containing a lot of JQuery, of which almost all of it works. All of it apart from:
private sortableOptions = {
JSPA: this,
delay: 50,
group: "favourites",
connectWith: ".sel-favourite-groups",
vertical: false,
itemSelector: "app-favourite",
placeholder: "<li class='placeholder favourite list-group-item' style=''></li>",
tolerance: 15,
onDrop: this.sortDrop,
onDragStart: this.sortDragStart
};
$list.sortable(this.sortableOptions);
This however does work if I refresh the page (press f5 etc).
When not working (on first page load) what I see is that list items are movable(you can click and drag them around), the event onDragStart fires, and the event onDrop fires. However while moving them no placeholder is displayed, and the other elements don't shift position. On drop the moving element just returns to its original location.
The relevant code in question in full is:
private render(): JQuery {
let $page = this.getTemplate("home");
let $list = $page.find("#home-favourites");
$list.sortable(this.sortableOptions);
try {
for (let f = 0; f < this.user.UserSettings.favourites.sites.length; f++) {
this.drawFavourite($list, this.user.UserSettings.favourites.sites[f]);
}
// and do the same for the groups
for (let g = 0; g < this.user.UserSettings.favourites.groups.length; g++) {
let group = this.user.UserSettings.favourites.groups[g];
// add the header and list to the DOM
let $list = this.drawFavouriteGroup(group);
$list.sortable(this.sortableOptions);
if (group.state == "minimised") {
let $li = $list.parent();
this.drawMinimisedFavouriteGroup($li, group.name);
$li.addClass("collapse");
}
for (let f = 0; f < group.sites.length; f++) {
this.drawFavourite($list, group.sites[f]);
}
}
}
catch (ex) {
console.log("EXCEPTION:: Home.render(): ", ex);
}
return $page;
}
From reviewing other similar issues (normally that JQuery things DON'T work after a refresh) I have already tried:
Adding everything to the DOM then calling .sortable()
Calling .sortable('refresh')
Simplifying the code so the 'groups' above(multiple <ul />s aren't included
None of these made any difference - never works on first load, always works after page refresh
Also navigating away from the page, and back means the .sortable() doesn't work.
What I can't understand is how a refresh can make it work, shouldn't an initial page load when navigated to - with no cached content - be identical to hitting f5?
As I said at the start, there is A LOT of other jQuery on the page all of which works as expected.
Anyone got any idea what might be going on here?

Related

Prepending Old Chat Messages to Chat Box

I'm trying to prepend old messages to a chatbox when the user scrolls to the top. I'm using eventListeners as the code illustrates, but I'm running into an error where only the last chatbox is working properly. It seems that bodies[index].scrollTop and bodies[index].scrollHeight always returns 0 with the exception of bodies[lastIndex]. Why might this be? (logging bodies[index] correctly returns the div element)
document.querySelectorAll('.popup-messages').forEach((item, index) =>
{
item.addEventListener('scroll', async function()
{
if (chatReady[index] == true && bodies[index].scrollTop == 0)
{
chatReady[index] = false;
var previousHeight = bodies[index].scrollHeight;
await getMessages(item.id.replace(":body", ""));
var heightDiff = bodies[index].scrollHeight - previousHeight;
bodies[index].scrollTop += heightDiff;
}
})
})
Edit: If there's a different way to make multiple eventListeners dynamically, please share it with me as it would help a lot.
This problem was solved by replacing bodies[index] with item. Perhaps there was a bug that occurs when more than one variable is associated with an element.

Infinite Scroll (JS) + iPhone - Images not loading

So I am using the Infinite Scroll script to prevent the user having to move on to the next page to view more products in Woocommerce. Everything works great except...
On iPhone (and iPad) the images that would technically be on the next page do not load at all once "Load More" initiates... so for example, if I have 1-8 products visible and I scroll down - the rest of the products appear, but only the images for the initial 1-8 products load, but the rest do not.
I have tried implementing bug fixes to no avail... just wondering if anyone else has encountered this and managed to find a fix?
<script>
var elem = document.querySelector('.shop-section');
var infScroll = new InfiniteScroll( elem, {
// options
path: '.woocommerce-pagination a.next',
append: '.products',
hideNav: '.woocommerce-pagination',
history: false,
status: '.page-load-status',
});
// iPhone Bug Fix
infScroll.on( 'append', function( response, path, items ) {
for ( var i=0; i < items.length; i++ ) {
reloadSrcsetImgs( item[i] );
}
});
function reloadSrcsetImgs( item ) {
var imgs = item.querySelectorAll('img[srcset]');
for ( var i=0; i < imgs.length; i++ ) {
var img = imgs[i];
img.outerHTML = img.outerHTML;
}
}
</script>
EDIT: I should also add that I noticed an error in the console as follows:
Uncaught ReferenceError: item is not defined
at InfiniteScroll.<anonymous> ((index):970)
at InfiniteScroll.proto.emitEvent (infinite-scroll.js?ver=1:254)
at InfiniteScroll.proto.dispatchEvent (infinite-scroll.js?ver=1:688)
at InfiniteScroll.<anonymous> (infinite-scroll.js?ver=1:1069)
at InfiniteScroll.proto.appendNextPage (infinite-scroll.js?ver=1:1076)
at InfiniteScroll.proto.onPageLoad (infinite-scroll.js?ver=1:1052)
at InfiniteScroll.<anonymous> (infinite-scroll.js?ver=1:1029)
at XMLHttpRequest.req.onload (infinite-scroll.js?ver=1:1249)

Two columned layout with multiple different height widgets, how to make widgets collapsable and keep flow of the page?

I have a main page that I'm working on and that is 2 columns with equal widths, with multiple widgets on the page. The page is responsive and the number of widgets is variable. At the moment I have a solution which works quite well and doesn't use a plugin like masonry or salvattore. The reason I've gone against using these plugins is that I don't want the page to be two rigid columns, I'd like the widgets to be able to flow and fit the available space.
This brings me onto my question - I'd like to be able to collapse any of these widgets and the other widgets should flow around and fit the space left. This is a prototype of what I have so far:
http://codepen.io/charge-valtech/pen/bzJfj
This is the jquery I've written:
function layoutWidgets() {
console.log('layout widgets');
if ($(".left-widget").css("float") == "left") {
$('.left-widget').each(function (index, value) {
var widgetPosition = $(this).position().left;
if (widgetPosition >= 30) {
$(this).removeClass('left-widget').addClass('right-widget');
}
});
$('.right-widget').each(function (index, value) {
var widgetPosition = $(this).position().left;
if (widgetPosition <= 30) {
$(this).removeClass('right-widget').addClass('left-widget');
}
});
}
}
layoutWidgets();
$(window).resize(layoutWidgets);
$('.collapse').click(function () {
$(this).closest('.widget').toggleClass('collapsed');
});
But for some reason if I put the layoutWidgets function inside the click function for collapsing the widgets, it doesn't work.
Any ideas?
Either put your code in a document.ready function or include it in the footer after everything is loaded. You can't attach a click event to an element that doesn't exist yet.
The way I've solved this is to do a for loop in my click function which should iterate through the widgets and run layoutWidgets:
$('.collapse-expand').click(function () {
$(this).closest('.dashboard-panel').toggleClass('collapsed');
var n = 7;
for (var i = 0; i < n; i++) {
layoutWidgets();
}
});

Greasemonkey popup loop not waiting for load-event listener

I'm writing a Greasemonkey script to automatically delete my notifications from a site, based on words I enter into a search box.
The delete "button" is basically a link, so I'm trying to open the first link in a new tab. Then, after it loads enough, open the rest of the links, one by one, in that same tab.
I figured out how to get the links I needed and how to loop and manipulate them. I was able to grab the first delete-link and open it in a new tab. I added an event listener to make sure the page was loaded before going to the next link.
I finally made that work so added my search box and button. Then I had to figure out how to wrap the whole thing in the event listener again.
So, I now have the whole thing working, except only the last link loads.
All links are going to my waitFor function so they should open, so it seems the event listener isn't working so it goes through the loop too fast and only the last link loads.
How do I make this script not continue the loop until the previous loaded page is fully loaded?
Complete code except for box and button creation:
var mytable = document.getElementById ('content').getElementsByTagName ('table')[0]
var myrows = mytable.rows
//function openLinkInTab () {
//mywin2.close ();
//}
var mywin2;
mywin2 = window.open ("http://www.aywas.com/message/notices/test/", "my_win2");
var links;
var waitFor = function (i) {
links = myrows[i].cells[1].getElementsByTagName ("a");
mywin2 = window.open (links[0].href, "my_win2");
}
var delnotifs = function () {
var matching;
var toRemove;
toRemove = document.getElementById ('find').value;
alert (toRemove)
for (i = 0; i < 10; i++) {
matching = myrows[i].cells[0].innerHTML;
if (matching.indexOf (toRemove) > 0) {
mywin2.addEventListener ('load', waitFor (i), false);
}
}
}
searchButton.addEventListener ('click', delnotifs, true);
So, why isn't it waiting for `mywin2.addEventListener('load', waitFor(i), false);`? I have a feeling it's something extremely simple that I'm missing here, but I just can't see it.
I also tried mywin2.addEventListener('load', function(){waitFor(i)}, false); and it still does the same thing, so it's not a problem of being a call instead of a pointer.
Swapping mywin2.addEventListener('load', waitFor(i), false); for
if (mywin2.document.readyState === "complete") { waitFor(i)} doesn't work either.
And while I'm at it... every time I see code looping through a list like this it uses
for(i=1;i < myrows.length;i++)
Which was skipping the first link in the list since arrays start at zero. So my question is, if I switch 'i' to zero, and the loop only goes while 'i' is < length, doesn't that mean it won't go through the whole list? Shouldn't it be
for(i=0;i != myrows.length;i++)
When you open a popup (or tab) with window.open, the load event only fires once -- even if you "open" a new URL with the same window handle.
To get the load listener to fire every time, you must close the window after each URL, and open a new one for the next URL.
Because popups are asynchronous and you want to load these links sequentially, don't use a for() loop for that. Use the popup load status to "chain" the links.
Here is the code to do that. It pushes the links onto an array, and then uses the load event to grab and open the next link. You can see the code in action at jsFiddle. :
var searchButton = document.getElementById ('gmPopUpBtn');
var mytable = document.getElementById ('content').getElementsByTagName ('table')[0];
var myrows = mytable.rows;
var linksToOpen = [];
var mywin2 = null;
function delnotifs () {
var toRemove = document.getElementById ('find').value;
for (var J = 0, L = myrows.length; J < L; J++) {
var matching = myrows[J].cells[0].innerHTML;
if (matching.indexOf (toRemove) > 0) {
var links = myrows[J].cells[1].getElementsByTagName ("a");
linksToOpen.push (links[0].href); //-- Add URL to list
}
}
openLinksInSequence ();
};
function openLinksInSequence () {
if (mywin2) {
mywin2.close ();
mywin2 = null;
}
if (linksToOpen.length) {
var link = linksToOpen.shift ();
mywin2 = window.open (link, "my_win2");
mywin2.addEventListener ('load', openLinksInSequence, false);
}
}
searchButton.addEventListener ('click', delnotifs, true);
See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener.
The second argument of the addEventLister function must be a pointer to a function and not a call.

javascript crashing iPad browser

I have some javascript inside a function that creates and populates an image carousel. It works fine after activating it in a pop up window the first 5 or 6 times, but then it eventually crashes the browser. I think there's some kind of leak, like something inside of it needs to be deleted before it gets created again. I know it's the carousel because if I get rid of that part of the script, it no longer crashes.
Here's the carousel script:
/* carousel */
var carousel,
el,
i,
page,
slides;
carousel = new SwipeView('#wrapper', {
numberOfPages: slides.length,
hastyPageFlip: true
});
// Load initial data
for (i=0; i<3; i++) {
page = i==0 ? slides.length-1 : i-1;
el = document.createElement('span');
el.innerHTML = slides[page];
carousel.masterPages[i].appendChild(el)
}
carousel.onFlip(function () {
var el,
upcoming,
i;
for (i=0; i<3; i++) {
upcoming = carousel.masterPages[i].dataset.upcomingPageIndex;
if (upcoming != carousel.masterPages[i].dataset.pageIndex) {
el = carousel.masterPages[i].querySelector('span');
el.innerHTML = slides[upcoming];
}
}
});
This script runs every time I click a link that launches a floating window.
I found out that I needed to clear my wrapper div. In the beginning of my function call:
document.getElementById('wrapper').innerHTML = "";
Seems to work.

Categories