Jquery Tools & Scrollable - javascript

I have scrollable with the navigator plugin and history set to true. This is so that users can use id'd links to scroll to the selection of their choice in the scrollable list (about 50 items). I have circular set to true.
The items show in groups of 4. But the previous and next buttons only move by 1 item, whereas I want them to move by 4 items. How do I do this?
I can't group them in div pages since the indexed tab list then matches to these div pages rather than the contained items.
I tried creating a click handler with api.move(-2), but all kinds of strange things happen (doesn't pre-empt native scroll-by-one; rewinds instead of advancing, or advances and then rewinds in one sequence - not acceptable).
I'm obviously missing something basic. Pointers would be appreciated.
Thanks,
Henrik

Here's what I ended up doing: I grabbed the api and replaced the next and prev functions; disabled circular; added first and last buttons. Sort of a workaround/semi-solution. You can see the result at http://cityrinks.ca/wiki/wiki.php?n=ListOfRinks.TableOfRinks
$(function() {
$(".scrollable")
.scrollable({
vertical: true,
mousewheel: true,
circular:false
})
.navigator({
navi: "#rinktabs",
naviItem: 'a',
activeClass: 'current',
history: false
});
var api = $(".scrollable").data("scrollable");
api.next = function(time) {
index = this.getIndex();
size = this.getSize();
interval = size - index;
if (interval > 4) interval = 4;
return this.move(interval, time);
};
api.prev = function(time) {
index = this.getIndex();
if (index > 4) index = 4;
return this.move(-index, time);
};
$("#firstbutton").click(function() {
var api = $(".scrollable").data("scrollable");
return api.begin(api.speed);
});
$("#lastbutton").click(function() {
var api = $(".scrollable").data("scrollable");
return api.end(api.speed);
});
});

Related

Protractor break while in promise then()

I'm automating e2e tests on an angular app. We have virtual scrolls on few input elements (only 10-20 items are loaded at a time, scrolling down loads more items and delete the previous ones to improve performance).
I made a method to scroll until I find the DOM element and click on it. However, I don't know how I can stop looping after I clicked the element.
Here is what I've done so far :
findQuery(queryName){
this.getSavedQueryCount().then((queryCount)=>{ // Total nb of items
this.count().then((displayCount)=>{ //nb of items this screen can display
let count = 0;
let flag = 0;
let actualCount = displayCount + 1;
do
{
if(count % (actualCount) === 0 && count !== 0) count++; //handling weird behavior, index 0 never gets deleted so we increment by 1 to get the next item.
browser.executeScript('arguments[0].scrollIntoView({behavior: "smooth", block: "end"});', this.getSavedQuery(count % actualCount)); // scroll to elem at index
this.getSavedQueryByName(queryName).isPresent().then((bool)=>{ // clicking on elem if it's in the DOM
if(bool)
{
this.getSavedQueryByName(queryName).getAttribute('class').then((cl)=>{
if(!(cl === "selected"))//making sure to click only once so we don't deselect the item
{
this.getSavedQueryByName(queryName).click();
flag = 1;
}
});
}
});
count += 1;
} while(queryCount > count || flag);
});
});
}
Please don't judge the code I will refactor it after I get the answer to this question (don't take the weird index handling into account).
So far this method works but it never stops looping until queryCount > count. I want it to stop looping after I clicked the element too (with the flag var).
Thanks in advance.

jQuery appendTo() without reloading embedded content

I've written a stacking function with jQuery that takes container element .search-results, gathers all the .card elements within each .col element in the container and then restacks them across a number of columns, which are calculated based off the width of the container.
When the page is loaded, all cards are initialized in the first column and subsequent columns are hidden. The script then unhides however many columns it determines should exist and iterates through the columns and cards, using appendTo to evenly distribute the cards amongst the columns.
I've created a jsFiddle that demonstrates the process.
The script works great when it's called on page load. If I want to call the script after the page has loaded though, I encounter a serious problem. If I remove one of the cards and want to restack them so the distribution remains even, I must call the stacking function again. When I do so, it causes the embedded content (e.g. embedded Tweets) within the cards to be reloaded, which is undesirable. Note that when the page loads the Tweets are not initialized until after the stacking function has been called.
In my example I intentionally iterate over the tweet ids to reinitialize them using twttr.widgets.createTweet(), however if I omit that step, the Tweets disappear after the cards are restacked. If I inspect the cards I can see that the Tweet widget iframe is still present, but the iframe's body is empty.
Now obviously I could simply reinitialize my Tweets after I restack the cards but that would offer up a poor user experience since there is a delay when (re)initializing an embedded Tweet. I had previously posed a question related to this. I found that I can manually move cards around from the console using .clone() and appendTo() without reloading the embedded Tweets, however I've not had any luck refactoring my stacking function to take advantage of this behavior, which is why I'm asking this question.
Here is my stacking function:
function resizeColumns(layoutElem, ignoreRank, width) {
if (width === undefined) {
var colMin = 300;
} else {
var colMin = width;
}
var w = layoutElem.width();
var numCols = Math.floor(w / colMin);
if (numCols === 0) numCols = 1;
layoutElem.removeClass('cols-1 cols-2 cols-3 cols-4 cols-5 cols-6 cols-7 cols-8 cols-9 cols-10 cols-11 cols-12');
layoutElem.addClass('cols-' + numCols);
var cols = layoutElem.find('.col');
var cards = cols.find('.card');
var sortedCards = [];
cards.each(function(i) {
var rank;
if (!ignoreRank) {
rank = parseInt($(this).attr('rank'));
if (isNaN(rank)) rank = 1000000;
} else {
var o = parseInt($(this).attr('order'));
if (isNaN(o)) {
rank = i;
$(this).attr('order', i);
} else {
rank = o;
}
}
sortedCards.push({
rank: rank,
element: $(this)
});
});
sortedCards.sort(rankCompare);
var curCol = 0;
for (var i in sortedCards) {
var cardElem = sortedCards[i].element;
cardElem.appendTo($(cols[curCol]));
curCol++;
if (curCol >= numCols) curCol = 0;
}
// hide any additional columns
cols.each(function(i) {
if (i >= numCols) {
$(this).hide();
} else {
$(this).show();
}
});
function rankCompare(a, b) {
if (a.rank < b.rank)
return -1;
if (a.rank > b.rank)
return 1;
return 0;
}
}
Steps I take to stack the cards and initialize the Tweets in the jsFiddle
The first time I call the function I introduce a delay so that $scope.cards is defined. Here is the first call:
$timeout(function() {
// Only the first argument is required.
resizeColumns($('.cards'), true, 250);
}, 250)
After I call the resizeColumns function I set a delay before initializing the tweets for the first time. This is essential so that the Twitter widgets script can locate the Tweets in the DOM.
Once the Tweets are initialized, I set a delay of 4s before removing a card and restacking the cards. Finally, once the cards are restacked, I reinitialize the Tweets. This last step is what I want to avoid. I don't want to have to reinitialize the Tweets because it's a slow process. I want the Tweet to reappear instantly once the card is moved.
$timeout(function() {
// Iterate over the Tweet IDs and initialize them.
ids.forEach(function(id) {
twttr.widgets.createTweet(id, document.getElementById(id));
});
$timeout(function() {
// Wait a few seconds and then remove a card from the DOM.
$('#' + $scope.cards[1].id).detach();
// Restack cards
resizeColumns($('.cards'), true, 250);
$timeout(function() {
// Reinitialize Tweets after cards have been restacked. This is the step I want to avoid.
ids.forEach(function(id) {
twttr.widgets.createTweet(id, document.getElementById(id));
});
}, 500)
}, 4000);
}, 1000);
Based on the answer to the other question, try changing:
sortedCards.push({
rank: rank,
element: $(this)
});
to:
sortedCards.push({
rank: rank,
element: $(this).clone();
});
$(this).detach();
Or you could do the clone and detach in the appendTo loop:
for (var i in sortedCards) {
var cardElem = sortedCards[i].element.clone();
cardElem.appendTo($(cols[curCol]));
sortedCards[i].element.detach()
curCol = (curCol + 1) % numCols;
}

Sliding Carousel - Pagination not directing to correct location

My carousel has been built so that it slides to the next frame automatically every 5 seconds, for which I have written:
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
function slide01() {
carousel.delay(5000).animate({'margin-left':'-100%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(2)').addClass('on');
});
slide02();
};
function slide02() {
carousel.delay(5000).animate({'margin-left':'-200%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(3)').addClass('on');
});
slide03();
};
function slide03() {
carousel.delay(5000).animate({'margin-left':'-300%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(4)').addClass('on');
});
slide04();
};
function slide04() {
carousel.delay(5000).animate({'margin-left':'0%'}, function() {
pagination.find('li').removeClass('on');
pagination.find(':nth-child(1)').addClass('on');
});
slide01();
};
slide01();
While this is working, I'm having difficulties with the pagination buttons. They respond but instead of going to its appropriate page, it's going to the next page, which you can view in my FIDDLE.
This is the code I have written for each button:
pagination.find('li:nth-child(1)').click(function(){
carousel.stop().animate({'margin-left':'0%'});
});
pagination.find('li:nth-child(2)').click(function(){
carousel.stop().animate({'margin-left':'100%'});
});
pagination.find('li:nth-child(3)').click(function(){
carousel.stop().animate({'margin-left':'200%'});
});
pagination.find('li:nth-child(4)').click(function(){
carousel.stop().animate({'margin-left':'300%'});
});
What can I do to fix this?
I have updated your Javascript code to provide your desired functionality and made it more extendable. Please let me know if you have any questions.
var carousel = $('.carousel ul:first');
var pagination = $('.carousel ul:last');
var slideWidth = 200;
var autoSlideTiming = 5000;
var timeout = null;
// Buttons
$.each(pagination.children(), function(i){
$(this).click(function(){
pagination.find('li.on').removeClass('on');
//if you wanted to find the width dynamically
//var slideWidth = $(this).parent().parent().find('ul:eq(0) li:eq('+i+')').width();
carousel.stop().animate({'margin-left': -(i * slideWidth)});
$(this).addClass('on');
});
});
// Slider
function advanceSlide(){
var currIndex = $(".pagination li.on").index();
var nextIndex = ((pagination.children().length - 1) == currIndex) ? 0 : (currIndex + 1);
carousel.animate({'margin-left': -(nextIndex * slideWidth)}, function() {
pagination.find('li.on').removeClass('on');
pagination.find(':nth-child('+(nextIndex + 1)+')').addClass('on');
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
}
$('.carousel').hover(function(){
clearTimeout(timeout);
}, function(){
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
});
//init auto slide
timeout = setTimeout(function(){advanceSlide()}, autoSlideTiming);
Also here is an updated Fiddle
Explanation
The $.each is a jQuery utility function that works like a normal for loop (I would actually recommend a vanilla JavaScript for loop if you know how). This $.each accepts 2 arguments: a collection (pagination.children() in our case, which is an array of the li's), and a callback function. This callback function passes in i which is our zero-based index of the collection. So, it's not 1, 2, 3, 4 but 0, 1, 2, 3. In this loop this is each li, which it is attaching a click event that handles removing and applying your on class and also the animation. The -(i * slideWidth) is taking the current zero-based index and multiplying it times the slideWidth and then getting the negative value of that result. So, the first slide -(0 * 200) would animate the margin-left to -0 or 0, and the second slide -(1 * 200) would animate the margin-left to -200, which pulls it 200 pixels in the left direction making it slide. This will allow you to had new li's to your pagination and this code will still work!
The trick to making the advanceSlide() function dynamic, is finding what the index of the next slide is. To find out where we need to go we first must find where we are. That is what currIndex is for, which we find with the jQuery index() function, which returns the zero-based index of the li with the on class. Now the magic. The nextIndex variable is being set using a ternary operator to find the value. A ternary is just programmer shorthand for a basic if then else statement. So if the ternary in this code was written out the normal way it would look like this:
var nextIndex;
if( (pagination.children().length - 1) == currIndex ){
nextIndex = 0;
}else{
nextIndex = currIndex + 1;
}
Remeber our currIndex is zero-based, but our collection (pagination.children()) length (number of items in the total collection) is not zero-based so we must minus 1 from it. Then we are checking if that value is equal to our currIndex because if this is the last item in our collection we need to set our nextIndex back to 0 so the slideshow loops back around. If it's not the last index we just add 1 to advance to the next slide. After we find that nextIndex we do our animation and apply our class.
I know this is a little long winded, but it's important that you fully understand the code you implement! Please let me know if you have nay further questions!
You have a "Maximum call stack size exceeded." error. Yours slide0X function calls another slide0Y function and so on. You should invoke slide0Y only once the slide0X animation ended (inside the complete callback).
Moreover in your click callbacks, margin-left should be negative since you want to slide to the left.
BTW, I would implement a function slideTo(slideId) so that it will work for n slides, instead of writing a function for each slide.

ReactJS: Modeling Bi-Directional Infinite Scrolling

Our application uses infinite scrolling to navigate large lists of heterogenous items. There are a few wrinkles:
It's common for our users to have a list of 10,000 items and need to scroll through 3k+.
These are rich items, so we can only have a few hundred in the DOM before browser performance becomes unacceptable.
The items are of varying heights.
The items may contain images and we allow the user to jump to a specific date. This is tricky because the user can jump to a point in the list where we need to load images above the viewport, which would push the content down when they load. Failing to handle that means that the user may jump to a date, but then be shifted to an earlier date.
Known, incomplete solutions:
(react-infinite-scroll) - This is just a simple "load more when we hit the bottom" component. It does not cull any of the DOM, so it will die on thousands of items.
(Scroll Position with React) - Shows how to store and restore the scroll position when inserting at the top or inserting at the bottom, but not both together.
I'm not looking for the code for a complete solution (although that would be great.) Instead, I'm looking for the "React way" to model this situation. Is scroll position state or not? What state should I be tracking to retain my position in the list? What state do I need to keep so that I trigger a new render when I scroll near the bottom or top of what is rendered?
This is a mix of an infinite table and an infinite scroll scenario. The best abstraction I found for this is the following:
Overview
Make a <List> component that takes an array of all children. Since we do not render them, it's really cheap to just allocate them and discard them. If 10k allocations is too big, you can instead pass a function that takes a range and return the elements.
<List>
{thousandelements.map(function() { return <Element /> })}
</List>
Your List component is keeping track of what the scroll position is and only renders the children that are in view. It adds a large empty div at the beginning to fake the previous items that are not rendered.
Now, the interesting part is that once an Element component is rendered, you measure its height and store it in your List. This lets you compute the height of the spacer and know how many elements should be displayed in view.
Image
You are saying that when the image are loading they make everything "jump" down. The solution for this is to set the image dimensions in your img tag: <img src="..." width="100" height="58" />. This way the browser doesn't have to wait to download it before knowing what size it is going to be displayed. This requires some infrastructure but it's really worth it.
If you can't know the size in advance, then add onload listeners to your image and when it is loaded then measure its displayed dimension and update the stored row height and compensate the scroll position.
Jumping at a random element
If you need to jump at a random element in the list that's going to require some trickery with scroll position because you don't know the size of the elements in between. What I suggest you to do is to average the element heights you already have computed and jump to the scroll position of last known height + (number of elements * average).
Since this is not exact it's going to cause issues when you reach back to the last known good position. When a conflict happens, simply change the scroll position to fix it. This is going to move the scroll bar a bit but shouldn't affect him/her too much.
React Specifics
You want to provide a key to all the rendered elements so that they are maintained across renders. There are two strategies: (1) have only n keys (0, 1, 2, ... n) where n is the maximum number of elements you can display and use their position modulo n. (2) have a different key per element. If all the elements share a similar structure it's good to use (1) to reuse their DOM nodes. If they don't then use (2).
I would only have two pieces of React state: the index of the first element and the number of elements being displayed. The current scroll position and the height of all the elements would be directly attached to this. When using setState you are actually doing a rerender which should only happen when the range changes.
Here is an example of infinite list using some of the techniques I describe in this answer. It's going to be some work but React is definitively a good way to implement an infinite list :)
have a look at http://adazzle.github.io/react-data-grid/index.html#
This looks like a powerful and performant datagrid with Excel-like features and lazy loading/optimized rendering (for millions of rows) with rich editing features (MIT licenced).
Not yet tried in our project but will do so pretty soon.
A great resource to search for things like these is also http://react.rocks/
In this case, a tag search is helpful:
http://react.rocks/tag/InfiniteScroll
I was facing a similar challenge for modeling single-direction infinite scrolling with heterogeneous item heights and so made an npm package out of my solution:
https://www.npmjs.com/package/react-variable-height-infinite-scroller
and a demo: http://tnrich.github.io/react-variable-height-infinite-scroller/
You can check out the source code for the logic, but I basically followed the recipe #Vjeux outlined in the above answer. I haven't yet tackled jumping to a particular item, but I'm hoping to implement that soon.
Here's the nitty-gritty of what the code currently looks like:
var React = require('react');
var areNonNegativeIntegers = require('validate.io-nonnegative-integer-array');
var InfiniteScoller = React.createClass({
propTypes: {
averageElementHeight: React.PropTypes.number.isRequired,
containerHeight: React.PropTypes.number.isRequired,
preloadRowStart: React.PropTypes.number.isRequired,
renderRow: React.PropTypes.func.isRequired,
rowData: React.PropTypes.array.isRequired,
},
onEditorScroll: function(event) {
var infiniteContainer = event.currentTarget;
var visibleRowsContainer = React.findDOMNode(this.refs.visibleRowsContainer);
var currentAverageElementHeight = (visibleRowsContainer.getBoundingClientRect().height / this.state.visibleRows.length);
this.oldRowStart = this.rowStart;
var newRowStart;
var distanceFromTopOfVisibleRows = infiniteContainer.getBoundingClientRect().top - visibleRowsContainer.getBoundingClientRect().top;
var distanceFromBottomOfVisibleRows = visibleRowsContainer.getBoundingClientRect().bottom - infiniteContainer.getBoundingClientRect().bottom;
var rowsToAdd;
if (distanceFromTopOfVisibleRows < 0) {
if (this.rowStart > 0) {
rowsToAdd = Math.ceil(-1 * distanceFromTopOfVisibleRows / currentAverageElementHeight);
newRowStart = this.rowStart - rowsToAdd;
if (newRowStart < 0) {
newRowStart = 0;
}
this.prepareVisibleRows(newRowStart, this.state.visibleRows.length);
}
} else if (distanceFromBottomOfVisibleRows < 0) {
//scrolling down, so add a row below
var rowsToGiveOnBottom = this.props.rowData.length - 1 - this.rowEnd;
if (rowsToGiveOnBottom > 0) {
rowsToAdd = Math.ceil(-1 * distanceFromBottomOfVisibleRows / currentAverageElementHeight);
newRowStart = this.rowStart + rowsToAdd;
if (newRowStart + this.state.visibleRows.length >= this.props.rowData.length) {
//the new row start is too high, so we instead just append the max rowsToGiveOnBottom to our current preloadRowStart
newRowStart = this.rowStart + rowsToGiveOnBottom;
}
this.prepareVisibleRows(newRowStart, this.state.visibleRows.length);
}
} else {
//we haven't scrolled enough, so do nothing
}
this.updateTriggeredByScroll = true;
//set the averageElementHeight to the currentAverageElementHeight
// setAverageRowHeight(currentAverageElementHeight);
},
componentWillReceiveProps: function(nextProps) {
var rowStart = this.rowStart;
var newNumberOfRowsToDisplay = this.state.visibleRows.length;
this.props.rowData = nextProps.rowData;
this.prepareVisibleRows(rowStart, newNumberOfRowsToDisplay);
},
componentWillUpdate: function() {
var visibleRowsContainer = React.findDOMNode(this.refs.visibleRowsContainer);
this.soonToBeRemovedRowElementHeights = 0;
this.numberOfRowsAddedToTop = 0;
if (this.updateTriggeredByScroll === true) {
this.updateTriggeredByScroll = false;
var rowStartDifference = this.oldRowStart - this.rowStart;
if (rowStartDifference < 0) {
// scrolling down
for (var i = 0; i < -rowStartDifference; i++) {
var soonToBeRemovedRowElement = visibleRowsContainer.children[i];
if (soonToBeRemovedRowElement) {
var height = soonToBeRemovedRowElement.getBoundingClientRect().height;
this.soonToBeRemovedRowElementHeights += this.props.averageElementHeight - height;
// this.soonToBeRemovedRowElementHeights.push(soonToBeRemovedRowElement.getBoundingClientRect().height);
}
}
} else if (rowStartDifference > 0) {
this.numberOfRowsAddedToTop = rowStartDifference;
}
}
},
componentDidUpdate: function() {
//strategy: as we scroll, we're losing or gaining rows from the top and replacing them with rows of the "averageRowHeight"
//thus we need to adjust the scrollTop positioning of the infinite container so that the UI doesn't jump as we
//make the replacements
var infiniteContainer = React.findDOMNode(this.refs.infiniteContainer);
var visibleRowsContainer = React.findDOMNode(this.refs.visibleRowsContainer);
var self = this;
if (this.soonToBeRemovedRowElementHeights) {
infiniteContainer.scrollTop = infiniteContainer.scrollTop + this.soonToBeRemovedRowElementHeights;
}
if (this.numberOfRowsAddedToTop) {
//we're adding rows to the top, so we're going from 100's to random heights, so we'll calculate the differenece
//and adjust the infiniteContainer.scrollTop by it
var adjustmentScroll = 0;
for (var i = 0; i < this.numberOfRowsAddedToTop; i++) {
var justAddedElement = visibleRowsContainer.children[i];
if (justAddedElement) {
adjustmentScroll += this.props.averageElementHeight - justAddedElement.getBoundingClientRect().height;
var height = justAddedElement.getBoundingClientRect().height;
}
}
infiniteContainer.scrollTop = infiniteContainer.scrollTop - adjustmentScroll;
}
var visibleRowsContainer = React.findDOMNode(this.refs.visibleRowsContainer);
if (!visibleRowsContainer.childNodes[0]) {
if (this.props.rowData.length) {
//we've probably made it here because a bunch of rows have been removed all at once
//and the visible rows isn't mapping to the row data, so we need to shift the visible rows
var numberOfRowsToDisplay = this.numberOfRowsToDisplay || 4;
var newRowStart = this.props.rowData.length - numberOfRowsToDisplay;
if (!areNonNegativeIntegers([newRowStart])) {
newRowStart = 0;
}
this.prepareVisibleRows(newRowStart , numberOfRowsToDisplay);
return; //return early because we need to recompute the visible rows
} else {
throw new Error('no visible rows!!');
}
}
var adjustInfiniteContainerByThisAmount;
//check if the visible rows fill up the viewport
//tnrtodo: maybe put logic in here to reshrink the number of rows to display... maybe...
if (visibleRowsContainer.getBoundingClientRect().height / 2 <= this.props.containerHeight) {
//visible rows don't yet fill up the viewport, so we need to add rows
if (this.rowStart + this.state.visibleRows.length < this.props.rowData.length) {
//load another row to the bottom
this.prepareVisibleRows(this.rowStart, this.state.visibleRows.length + 1);
} else {
//there aren't more rows that we can load at the bottom so we load more at the top
if (this.rowStart - 1 > 0) {
this.prepareVisibleRows(this.rowStart - 1, this.state.visibleRows.length + 1); //don't want to just shift view
} else if (this.state.visibleRows.length < this.props.rowData.length) {
this.prepareVisibleRows(0, this.state.visibleRows.length + 1);
}
}
} else if (visibleRowsContainer.getBoundingClientRect().top > infiniteContainer.getBoundingClientRect().top) {
//scroll to align the tops of the boxes
adjustInfiniteContainerByThisAmount = visibleRowsContainer.getBoundingClientRect().top - infiniteContainer.getBoundingClientRect().top;
// this.adjustmentScroll = true;
infiniteContainer.scrollTop = infiniteContainer.scrollTop + adjustInfiniteContainerByThisAmount;
} else if (visibleRowsContainer.getBoundingClientRect().bottom < infiniteContainer.getBoundingClientRect().bottom) {
//scroll to align the bottoms of the boxes
adjustInfiniteContainerByThisAmount = visibleRowsContainer.getBoundingClientRect().bottom - infiniteContainer.getBoundingClientRect().bottom;
// this.adjustmentScroll = true;
infiniteContainer.scrollTop = infiniteContainer.scrollTop + adjustInfiniteContainerByThisAmount;
}
},
componentWillMount: function(argument) {
//this is the only place where we use preloadRowStart
var newRowStart = 0;
if (this.props.preloadRowStart < this.props.rowData.length) {
newRowStart = this.props.preloadRowStart;
}
this.prepareVisibleRows(newRowStart, 4);
},
componentDidMount: function(argument) {
//call componentDidUpdate so that the scroll position will be adjusted properly
//(we may load a random row in the middle of the sequence and not have the infinte container scrolled properly initially, so we scroll to the show the rowContainer)
this.componentDidUpdate();
},
prepareVisibleRows: function(rowStart, newNumberOfRowsToDisplay) { //note, rowEnd is optional
//setting this property here, but we should try not to use it if possible, it is better to use
//this.state.visibleRowData.length
this.numberOfRowsToDisplay = newNumberOfRowsToDisplay;
var rowData = this.props.rowData;
if (rowStart + newNumberOfRowsToDisplay > this.props.rowData.length) {
this.rowEnd = rowData.length - 1;
} else {
this.rowEnd = rowStart + newNumberOfRowsToDisplay - 1;
}
// var visibleRows = this.state.visibleRowsDataData.slice(rowStart, this.rowEnd + 1);
// rowData.slice(rowStart, this.rowEnd + 1);
// setPreloadRowStart(rowStart);
this.rowStart = rowStart;
if (!areNonNegativeIntegers([this.rowStart, this.rowEnd])) {
var e = new Error('Error: row start or end invalid!');
console.warn('e.trace', e.trace);
throw e;
}
var newVisibleRows = rowData.slice(this.rowStart, this.rowEnd + 1);
this.setState({
visibleRows: newVisibleRows
});
},
getVisibleRowsContainerDomNode: function() {
return this.refs.visibleRowsContainer.getDOMNode();
},
render: function() {
var self = this;
var rowItems = this.state.visibleRows.map(function(row) {
return self.props.renderRow(row);
});
var rowHeight = this.currentAverageElementHeight ? this.currentAverageElementHeight : this.props.averageElementHeight;
this.topSpacerHeight = this.rowStart * rowHeight;
this.bottomSpacerHeight = (this.props.rowData.length - 1 - this.rowEnd) * rowHeight;
var infiniteContainerStyle = {
height: this.props.containerHeight,
overflowY: "scroll",
};
return (
<div
ref="infiniteContainer"
className="infiniteContainer"
style={infiniteContainerStyle}
onScroll={this.onEditorScroll}
>
<div ref="topSpacer" className="topSpacer" style={{height: this.topSpacerHeight}}/>
<div ref="visibleRowsContainer" className="visibleRowsContainer">
{rowItems}
</div>
<div ref="bottomSpacer" className="bottomSpacer" style={{height: this.bottomSpacerHeight}}/>
</div>
);
}
});
module.exports = InfiniteScoller;

Slide menu with javascript

I'm trying to create a new menu with job listings for my wordpress page, like the one on this page: http://www.resume.se/, where it says Senaste jobben (sorry for it being in Swedish, hope you get which menu I'm referring to). So, I want the list object to be visible for a few seconds, and then I want the menu slide to the left, the first time the width of the first list object, the second slide the width of the second list object, and so on.
if ($('.container-menu-platsannonser').length > 0 ) {
var slider = $('.menu-platsannonser').children('ul');
speed = 4000;
slider.each(function(){
var $this = $(this);
var li = $this.find('li');
var no_of = li.length;
var cur = 1;
if (3 < no_of) {
$this.slideShow = function() {
++cur;
$this.animate({
'margin-left': '-='+100
}, 600, 'swing', function(){
if (no_of == cur) {
$this.css('margin-left', '0');
cur = 1;
}
setTimeout($this.slideShow, speed);
})
}
setTimeout($this.slideShow, speed);
}
})
}
I've managed to set the slide to 100 px, but how do I get the animation width to change with each list item? Also, right now I have four menu items, created with wordpress. This code makes the list items slide two and a half time, and then it starts from the beginning. I would like the menu to loop seamlessly. Bear with me on this, since I kinda suck at javascript. Please, let me know if I'm being unclear, or if you need additional information.
So, to sum up. What I'd like to do is:
– Slide the menu to the left, the width of the list object
– Keep on looping endlessly
Appreciate any help I can get!
To get the width on the list item I think you can just use:
$("li:fist").outerWidth(true);
To keep the loop going forever I would wait for the animation to complete and move the li to the end of the list.
var $li = $("li:first");
$li.parent().append($li);

Categories