backbone: how to pass data to a render view - javascript

is there a way to pass data to render
The following code is what I'm currently using:
render:function() {
var background =
var highlight = this.model.get('highlight');
$(this.el).find('div').each(function(index,element) {
if(index < highlight) {
$(element).css({'background': 'url("assets/img/alphabet/ok/ok.png"), url('+background+')'});
$(element).attr('id', 'ok');
} else {
$(element).removeAttr('id');
}
})
}
maybe like this pic:

... you can do something like this:
this.background : [],
//...
initialize : function(){
//...
this.background.push(string.charAt(i));
//...
},
render : function(){
//...
var background = this.background[index];
//...
}
but, maybe also for loop inside your initialize can stay inside render, because it render the view (with .append()).
EDIT NOTE:
I repeat, the possibilities to move around the loop in the initialization function in the render function.
I try to change your backbone view structure:
var WordView = Backbone.View.extend({
initialize : function(){
this.listenTo(this.model,"remove",this.remove);
},
render : function(){
var $Div,
string = this.model.get('string'), //use _string (string can cause some issue in IE)
highlight = this.model.get("highlight"),
letter_width = 36,
letter_heigth = 35,
word_width = string.length * letter_width;
if(this.model.get("x") + word_width > $(window).width()) this.model.set({x:$(window).width() - word_width});
$(this.el).css({
position:"absolute",
top : this.model.get("y"),
left : this.model.get("x")
});
var background,char;
for(var i; i < string.length; i++){
char = string.charAt(i);
background = "url('"+char+"') no-repeat";
$Div.removeAttr("id"); //seems useless
if(i < highlight){
background = "url('assets/img/alphabet/ok/ok.png'),url('"+char+"')";
$Div.id("ok"); //maybe is better $Div.class("ok");
}
$Div = $("<div>").clone();
$Div.css({
width : letter_width,
heigth : letter_height,
float: "left",
"background" : background
}).appendTo($(this.el));
}
}
});

Related

Trouble styling a DIV with two background images using Javascript Objects

I can only get the CSS styled image to load. The browser does not display imgB. The source code also only shows imgA. I just want to call the backgroundImage value of the Object at the time of the specific loop
My HTML.
<div onclick="displayCards()">Display Cards</div>
My Javascript Objects contain properties for these DIV elements. The different cards each have a unique image imgB. I created an array for the Objects so I can loop through them with a for loop.
var card_troop = { title : "troop" , type : "Unit" , image : "url(troop.png)" } ;
var card_base = { title : "base" , type : "Structure" , image : "url(base.png)" } ;
var card_wood = { title : "wood" , type : "Resource" , image : "url(wood.png)" } ;
var list = [
card_troop ,
card_base ,
card_wood
] ;
The DIV elements are created by my displayCards() function, that are appended to the body of my HTML document.
function displayCards()
{
card = document.createElement("div") ;
var card_id = document.createAttribute("id") ;
card_id.value = list[x].title ;
card.setAttributeNode(card_id) ;
var card_class = document.createAttribute("class") ;
switch (list[x].type) // Card Theme Comparison
{
case "Unit" :
card_class.value = "unit" ;
break ;
case "Structure" :
card_class.value = "structure" ;
break ;
case "Resource" :
card_class.value = "resource" ;
break ;
}
card.setAttributeNode(card_class) ;
var imgA = card.style.backgroundImage ;
var imgB = list[x].image ;
card.style.backgroundImage = imgA + " , " + imgB ;
}
Using the switch Comparison, switch (list[x].type), defined by the Object Property type:, each DIV will have one of three CSS backgroundImage values.
div.unit { background-image:url( theme_unit.png ) ; }
div.structure { background-image:url( theme_structure.png ) ; }
div.resource { background-image:url( theme_resource.png) ; }
Another function builder() calls displayCards() and appends the Object card.
function builder()
{
for ( x = 0 ; x < 9 ; x++ )
{
displayCards() ;
document.body.appendChild(card) ;
}
}
I believe my problem lies here. Since the DIV is still not appended.
To note:imgA is a higher index than imgB and has transparency to allow imgB to be seen.
var imgA = card.style.backgroundImage ;
var imgB = list[x].image ;
card.style.backgroundImage = imgA + " , " + imgB ;
I am a minimalist coder and this is the shortest I could come up with. I do not want to have to repeat URL's more than once. My list of Objects is going to get much longer than three.
NO JQUERY
var card_troop = { title : "troop" , type : "Unit" , image : "url(troop.png)" } ;
var card_base = { title : "base" , type : "Structure" , image : "url(base.png)" } ;
var card_wood = { title : "wood" , type : "Resource" , image : "url(wood.png)" } ;
var list = [
card_troop,
card_base,
card_wood
];
function buildCard(card) {
var elem = document.createElement('div'),
elemCloned,
computedBackgroundUrl,
backgroundUrl;
elem.id = card.title;
elem.className = card.type.toLowerCase();
// Clone element and temporary append to body
elemCloned = elem.cloneNode();
elemCloned.style.display = 'none';
document.body.appendChild(elemCloned);
computedBackgroundUrl = window.getComputedStyle(elemCloned)['background-image'];
if (computedBackgroundUrl) {
var match = computedBackgroundUrl.match(/^url\((.*)\)/);
if (match) {
backgroundUrl = match[1];
}
}
// remove element after retrieved background-image value
elemCloned.parentNode.removeChild(elemCloned);
elemCloned = null;
elem.style.backgroundImage = (backgroundUrl ? 'url(' + backgroundUrl + '), ' : '') + card.image;
computedBackgroundUrl = null;
backgroundUrl = null;
return elem;
}
function displayCards() {
var body = document.body;
for (var i = 0, len = list.length; i < len; i++) {
body.appendChild( buildCard(list[i]) );
}
}
Never got efficient in js but your js solution should be the js parallel.
add/remove the class on the div and swap background images according to class in css. I'm assuming this would require less js as a bonus.
hope this helps.
You should work through this approach. Replace "backgroundimage" and "foregroundimage" with the appropriated URLs (of course).
Afterwards you can call the method showCard with a switch. showCard(true) shows the card's front. showCard(false) shows the card's back.
var card = function(title, type, image) {
this.title = title;
this.type = type;
this.image = image;
this.background = "url('backgroundimage')";
};
card.prototype.showCard = function(turn) {
if (turn) {
var ele = document.createElement("div");
ele.appendChild(document.createTextNode(this.title));
ele.appendChild(document.createTextNode(this.type));
ele.style.backgroundImage = this.image;
document.body.appendChild(ele);
} else {
var ele = document.createElement("div");
ele.style.backgroundImage = this.background;
document.body.appendChild(ele);
}
};
function displayCards() {
var card_troop = new card("TroopTitle", "TroopType", "url('foregroundimage')");
card_troop.showCard(true);
var card_troop_2 = new card("Troop2Title", "TroopType", "url('foregroundimage2')");
card_troop_2.showCard(false);
};
div {
min-width: 50px;
height: 120px;
border: solid;
float: left;
}
<input type="button" onclick="displayCards()" value="Display Cards" />

Is it possible to write legend to a separate division with jqPlot

I want to write the legend to a separate div, is this possible with jqPlot
legend: {
show: true,
placement: 'outside',
fontSize: '11px',
location: 'n'
}
Here our the two function which you can use:
function graphLegendCreation(cnt){
var parentNode = _g("dmGraphLegend")//trying to get the div element with id dmGraphLegend
, newLegendItemNode = Util.ce("span") //creating an span element
, newLegendItemSelect = Util.cep("input", { //creating an input element
"type":"checkbox",
"name":"graphLegend",
"checked":true,
"value":cnt
})
, newLegendItemIconNode = Util.cep("canvas", {
"id":"series_icon_"+cnt,
"className":"series_icons"
});
newLegendItemIconNode.style.display = "inline-block";
newLegendItemIconNode.style.position = "relative";
if(parentNode) {
newLegendItemNode.innerHTML = graphPlot.series[cnt].label;
newLegendItemSelect = Util.ac(newLegendItemSelect,parentNode);
newLegendItemSelect.checked = true;
Util.addEvent(newLegendItemSelect,"click", function(e) {
graphPlot.series[this.value].show = newLegendItemSelect.checked;
graphPlot.redraw(false);
})
newLegendItemIconNode = Util.ac(newLegendItemIconNode,parentNode);
newLegendItemNode = Util.ac(newLegendItemNode,parentNode);
Util.ac(Util.ce("br"),parentNode);
}
}
function showlegend() {
var cntr = 0
,len = 0
,iconNodes
,legendItemIconNode
,seriesSequence = 0
,context
,bMarker;
iconNodes = Util.Style.g("series_icons");
len = iconNodes.length;
for(cntr = 0; cntr < len; cntr++) {
legendItemIconNode = iconNodes[cntr];
if ($.browser.msie) {
G_vmlCanvasManager.initElement(legendItemIconNode);
}
context = legendItemIconNode.getContext('2d');
bMarker = new nMarkerRenderer({
size:8,
color:graphPlot.series[cntr].color,
style:graphPlot.series[cntr].markerOptions.style
});
bMarker.draw(12,12,context, {});
}
}
CSS:
.series_icons{width:20px;height:20px;}
GraphLegendCreation function you have to call that each time you create a entry inside legend.
ShowLegend is just going to create that legend icons.
The "nMarkrenderer" is already a defined function. Get that function from jsfiddle file
Let me know you want more help with this. It works for sure on IE. tried and tested

How to apply a javascript function to a multiple div Classes?

I have a function that creates a gallery of flickr sets pulled from my flickr account. I am getting the set numbers from a database and using a while loop to display the first image from the set. Each element of the table has the same class and i am applying a Javascript function to them. Unfortunately each table element is displaying the same photo, the last one pulled from the database.
$(document).ready(function() {
var flickrUrl="";
$('.gallery_table_data').each(function(){
flickrUrl = $(this).attr('title');
$('.flickr_div').flickrGallery({
"flickrSet" : flickrUrl,
"flickrKey" : "54498f94e844cb09c23a76808693730a"
});
});
});
and the images dont show up at all? can anyone help??
Here is the flickr jquery in case that's the problem:
var flickrhelpers = null;
(function(jQuery) {
jQuery.fn.flickrGallery = function(args) {
var $element = jQuery(this), // reference to the jQuery version of the current DOM element
element = this; // reference to the actual DOM element
// Public methods
var methods = {
init : function () {
// Extend the default options
settings = jQuery.extend({}, defaults, args);
// Make sure the api key and setID are passed
if (settings.flickrKey === null || settings.flickrSet === null) {
alert('You must pass an API key and a Flickr setID');
return;
}
// CSS jqfobject overflow for aspect ratio
element.css("overflow","hidden");
// Get the Flickr Set :)
$.getJSON("http://api.flickr.com/services/rest/?format=json&method=flickr.photosets.getPhotos&photoset_id=" + settings.flickrSet + "&api_key=" + settings.flickrKey + "&jsoncallback=?", function(flickrData){
var length = 1;
var thumbHTML = '';
for (i=0; i<length; i++) {
var photoURL = 'http://farm' + flickrData.photoset.photo[i].farm + '.' + 'static.flickr.com/' + flickrData.photoset.photo[i].server + '/' + flickrData.photoset.photo[i].id + '_' + flickrData.photoset.photo[i].secret +'.jpg'
settings.imgArray[i] = photoURL;
settings.titleArray[i] = flickrData.photoset.photo[i].title;
}
// Get the position of the element Flickr jqfobj will be loaded into
settings.x = element.offset().left;
settings.y = element.offset().top;
settings.c = settings.x + (element.width() / 2);
settings.ct = settings.y + (element.height() / 2);
// When data is set, load first image.
flickrhelpers.navImg(0);
});
}
}
// Helper functions here
flickrhelpers = {
navImg : function (index) {
// Set the global index
currentIndex = index;
// Create an image Obj with the URL from array
var thsImage = null;
thsImage = new Image();
thsImage.src = settings.imgArray[index];
// Set global imgObj to jQuery img Object
settings.fImg = $( thsImage );
// Display the image
element.html('');
element.html('<img class="thsImage" src=' + settings.imgArray[index] + ' border=0>');
// Call to function to take loader away once image is fully loaded
$(".thsImage").load(function() {
// Set the aspect ratio
var w = $(".thsImage").width();
var h = $(".thsImage").height();
if (w > h) {
var fRatio = w/h;
$(".thsImage").css("width",element.width());
$(".thsImage").css("height",Math.round(element.width() * (1/fRatio)));
} else {
var fRatio = h/w;
$(".thsImage").css("height",element.height());
$(".thsImage").css("width",Math.round(element.height() * (1/fRatio)));
}
if (element.outerHeight() > $(".thsImage").outerHeight()) {
var thisHalfImage = $(".thsImage").outerHeight()/2;
var thisTopOffset = (element.outerHeight()/2) - thisHalfImage;
$(".thsImage").css("margin-top",thisTopOffset+"px");
}
if (settings.titleArray[currentIndex] != "") {
$(".flickr_count").append(settings.titleArray[currentIndex]);
}
});
},
toggleUp : function() {
$("#flickr_thumbs").slideUp("slow");
}
}
// Hooray, defaults
var defaults = {
"flickrSet" : null,
"flickrKey" : null,
"x" : 0, // Object X
"y" : 0, // Object Y
"c" : 0, // Object center point
"ct" : 0, // Object center point from top
"mX" : 0, // Mouse X
"mY" : 0, // Mouse Y
"imgArray" : [], // Array to hold urls to flickr images
"titleArray" : [], // Array to hold image titles if they exist
"currentIndex" : 0, // Default image index
"fImg" : null, // For checking if the image jqfobject is loaded.
}
// For extending the defaults!
var settings = {}
// Init this thing
jQuery(document).ready(function () {
methods.init();
});
// Sort of like an init() but re-positions dynamic elements if browser resized.
$(window).resize(function() {
// Get the position of the element Flickr jqfobj will be loaded into
settings.x = element.offset().left;
settings.y = element.offset().top;
settings.c = settings.x + (element.width() / 2);
settings.ct = settings.y + (element.height() / 2);
});
}
})(jQuery);
The big problem is in your $.each loop. I am going to assume the plugin will work for all the elements you are looping over although have doubts that it will.
WHen you select $('.flickr_div') on each pass it affects all the elements in page with that class...so only the last pass of loop is valid
$(document).ready(function() {
var flickrUrl="";
$('.gallery_table_data').each(function(){
flickrUrl = $(this).attr('title');
/* this is your problem , is selecting all ".flickr_div" in page on each loop*/
//$('.flickr_div').flickrGallery({
/* without seeing your html structure am assuming
next class is inside "this"
try: */
$(this).find('.flickr_div').flickrGallery({
"flickrSet" : flickrUrl,
"flickrKey" : "54498f94e844cb09c23a76808693730a"
});
});
});
EDIT This same concept of using find() should also be refactoered into code within the plugin. Plugin should have all ID's replaced with classes.
Plugin really does not look well constructed for multiple instances within a page
I might be wrong here, but won't this (in your flickrGallery object)
$("body").append('<div id="flickr_loader"></div>');`
create multiple elements with the same ID? And the same for images in flickrhelpers:
element.html('<img id="thsImage" src=' + settings.imgArray[index] + ' border=0>');

Javascript - Designpattern suggestion needed

Hallo,
I have 3 Different function in Javascript, the first one replaces HTML Selectboxs width custom selectbox created with ULs.
and the other 2 replace Checkbox and Radio buttons respectivly.
Now I want to derive classes out of these functions, and need your suggestions, what will be the best way to organize these functions into class, whether inheretance is possible?
I really appriciate your help.
Thanks.
Here is some sample code.
function replaceSelect(formid) {
var form = $(formid);
if (!form) return;
invisibleSelectboes = document.getElementsByClassName("optionsDivInvisible");
if (invisibleSelectboes.length > 0) {
for (var i = 0; i < invisibleSelectboes.length; i++) {
document.body.removeChild(invisibleSelectboes[i]);
}
}
var selects = [];
var selectboxes = form.getElementsByTagName('select');
var selectText = "Bitte auswählen";
var selectRightSideWidth = 21;
var selectLeftSideWidth = 8;
selectAreaHeight = 21;
selectAreaOptionsOverlap = 2;
// Access all Selectboxes in Search mask.
for (var cfs = 0; cfs < selectboxes.length; cfs++) {
selects.push(selectboxes[cfs]);
}
// Replace the select boxes
for (var q = 0; q < selects.length; q++) {
if (selects[q].className == "") continue;
var onchangeEvent = selects[q].onchange;
//create and build div structure
var selectArea = document.createElement('div');
var left = document.createElement('div');
var right = document.createElement('div');
var center = document.createElement('div');
var button = document.createElement('a');
// var text = document.createTextNode(selectText);
var text = document.createTextNode('');
center.id = "mySelectText" + q;
if ( !! selects[q].getAttribute("selectWidth")) {
var selectWidth = parseInt(selects[q].getAttribute("selectWidth"));
} else {
var selectWidth = parseInt(selects[q].className.replace(/width_/g, ""));
}
center.style.width = selectWidth + 'px';
selectArea.style.width = selectWidth + selectRightSideWidth + selectLeftSideWidth + 'px';
if (selects[q].style.display == 'none' || selects[q].style.visibility == 'hidden') {
selectArea.style.display = 'none';
}
button.style.width = selectWidth + selectRightSideWidth + selectLeftSideWidth + 'px';
button.style.marginLeft = -selectWidth - selectLeftSideWidth + 'px';
// button.href = "javascript:toggleOptions( + q + ")";
Event.observe(button, 'click', function (q) {
return function (event) {
clickObserver(event, q)
}
}(q));
button.onkeydown = this.selectListener;
button.className = "selectButton"; //class used to check for mouseover
selectArea.className = "selectArea";
selectArea.id = "sarea" + q;
left.className = "left";
right.className = "right";
center.className = "center";
right.appendChild(button);
center.appendChild(text);
selectArea.appendChild(left);
selectArea.appendChild(right);
selectArea.appendChild(center);
//hide the select field
selects[q].style.display = 'none';
//insert select div
selects[q].parentNode.insertBefore(selectArea, selects[q]);
//build & place options div
var optionsDiv = document.createElement('div');
if (selects[q].getAttribute('width')) optionsDiv.style.width = selects[q].getAttribute('width') + 'px';
else optionsDiv.style.width = selectWidth + 8 + 'px';
optionsDiv.className = "optionsDivInvisible";
optionsDiv.id = "optionsDiv" + q;
optionsDiv.style.left = findPosX(selectArea) + 'px';
optionsDiv.style.top = findPosY(selectArea) + selectAreaHeight - selectAreaOptionsOverlap + 'px';
//get select's options and add to options div
for (var w = 0; w < selects[q].options.length; w++) {
var optionHolder = document.createElement('p');
if (selects[q].options[w].className == "informal") {
var optionLink = document.createElement('a');
var optionTxt = document.createTextNode(selects[q].options[w].getAttribute('text'));
optionLink.innerHTML = selects[q].options[w].getAttribute('text');
optionLink.className = "informal";
cic.addEvent(optionLink, 'click', function (event) {
Event.stop(event);
});
Event.observe(optionLink, 'mouseover', function (event) {
Event.stop(event);
});
Event.observe(optionLink, 'mouseout', function (event) {
Event.stop(event);
});
}
else {
var optionLink = document.createElement('a');
var optionTxt = document.createTextNode(selects[q].options[w].text);
optionLink.appendChild(optionTxt);
cic.addEvent(optionLink, 'click', function (id, w, q, onchangeEvent) {
return function () {
showOptions(q);
selectMe(selects[q].id, w, q, onchangeEvent);
}
}(selects[q].id, w, q, onchangeEvent));
}
//optionLink.href = "javascript:showOptions(" + q + "); selectMe('" + selects[q].id + "'," + w + "," + q + ");";
optionHolder.appendChild(optionLink);
optionsDiv.appendChild(optionHolder);
if (selects[q].options[w].selected) {
selectMe(selects[q].id, w, q);
}
}
document.getElementsByTagName("body")[0].appendChild(optionsDiv);
Event.observe(optionsDiv, 'mouseleave', function (submenuid) {
optionsDiv.className = 'optionsDivInvisible'
});
cic.addEvent(optionsDiv, 'click', function (event) {
if (event.stopPropagation) event.stopPropagation();
else event.cancelBubble = true;
});
}
form.setStyle({
visibility: 'visible'
});
}​
From the sounds of it, you're looking to create a unified API to encapsulate all of this "form enhancing" functionality. Possibly something like this:
var formEnhancement = {
SelectBox: function(){ /* ... */ },
CheckBox: function(){ /* ... */ },
RadioButton: function(){ /* ... */ }
};
formEnhancement.SelectBox.prototype = { /* ... define methods ... */ };
// etc. (other prototypes)
// Call something:
var myEnhancedSelectBox = new formEnhancement.SelectBox(
document.getElementById('id-of-a-select-box')
);
Does this answer your query?
I'd go with
var Library = (function()
{
function _selectBox()
{
// stuff
}
function _checkBox()
{
// stuff
}
function _radioButton()
{
// stuff
}
return {
SelectBox : _selectBox,
CheckBox : _checkBox,
RadioButton : _radioButton
};
})();
or
var Library = (function()
{
return {
SelectBox : function()
{
// stuff
},
CheckBox : function()
{
// stuff
},
RadioButton : function()
{
// stuff
}
};
})();
[Edit]
this way, you can actually declare "private" variables that can be accessible only from the library itself, just declaring var foo="bar"; inside Library's declaration, makes a foo variable that can't be accessed from outside, but can be accessed by anything within Library, this is why functions like _selectBox in my example remain private, but can still be accessed through Library.SelectBox, which would be the "public getter"
[/Edit]
also, instead of
var Library = (function(){})();
you could do something like this:
var Library = Library || {};
Library.UI = (function(){})();
this way, you can keep separate parts of your code library, you can keep them in separate files, which don't care about the order in which they are loaded, as long as they have
var Library = Library || {};
on top of them
the functions would then be called like this:
Library.SelectBox();
or in the case you chose to go with "subclasses"
Library.UI.SelectBox();
All the answers are general patterns I think none of them is really helpful. Just because you put your 3 huge function into an object doesn't make your code modular, reusable, maintainable.
So my first suggestion is to utilize function decomposition. You've mentioned inheritance. Now if your code is basically made of this 3 giant functions nothing can be inherited or shared. You should separate function logic by purpose into smaller, more straighforward ones.
A good example is that you've mentioned the word replacing is relevant in all your cases. Maybe you can set up a function that is responsible for DOM replacement independently of the element's type. Such function can be shared between your modules making your code more robust and allowing you to DRY.
The best way to organize this process is called wishful thinking, when you solve your problem with functions which are intuitive and helpful even though they may not even exist. This is related to how you can design effective interaces.
Put the functions in a namespace:
Declare it like this:
FormUtils = {};
and add its properties, which will be your functions
FormUtils.replaceSelect = function () {/*your code*/};
FormUtils.replaceCheckbox = function () {/*your code*/};
FormUtils.replaceRadio = function () {/*your code*/};
then you call this functions with their namespace:
FormUtils.replaceSelect();
This is a simple and very accepted design pattern to javascript

Loading content with ajax while scrolling

I'm using jQuery Tools Plugin as image slider (image here), but due to large amount of images I need to load them few at a time. Since it's javascript coded, I can't have the scroll position as far as I know. I want to load them as soon as the last image shows up or something like that. I have no idea where I put and event listener neither anything.
Here is my code http://jsfiddle.net/PxGTJ/
Give me some light, please!
I just had to use jQuery Tools' API, the onSeek parameter within the scrollable() method.
It was something like that
$(".scrollable").scrollable({
vertical: true,
onSeek: function() {
row = this.getIndex();
// Check if it's worth to load more content
if(row%4 == 0 && row != 0) {
var id = this.getItems().find('img').filter(':last').attr('id');
id = parseInt(id);
$.get('galeria.items.php?id='+id, null, function(html) {
$('.items').append(html);
});
}
}
});
That could be made the following way:
//When the DOM is ready...
$(document).ready(function() {
//When the user scrolls...
$(window).scroll(function() {
var tolerance = 800,
scrollTop = $(window).scrollTop();
//If the the distance to the top is greater than the tolerance...
if(scrollTop > tolerance) {
//Do something. Ajax Call, Animations, whatever.
}
}) ;
});
That should do the trick.
EDIT: Because you're not using the native scroll, we've got to do a little fix to the code:
//When the DOM is ready...
$(document).ready(function() {
//When the user scrolls...
$("div.scrollable").find(".next").click(function() {
var tolerance = 800,
// The absolute value of the integer associated
// to the top css property
scrollTop = Math.abs(parseInt($("div.items").css("top")));
//If the the distance to the top is greater than the tolerance...
if(scrollTop > tolerance) {
//Do something. Ajax Call, Animations, whatever.
}
}) ;
});
try something like this
$('#scrollable').find('img:last').load(function() {
//load the content
});
OR find the offset location/position of the last image and try loading your content when you reach the offset position on scrolling
HTML :
<div>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<span>Hello !!</span>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</div>
some CSS :
div {
width:200px;
height:200px;
overflow:scroll;
}
Javascript :
$(document).ready(function() {
$('div').scroll(function() {
var pos = $('div').scrollTop();
var offset = $('span').offset().top;
if(pos >= offset ) {
alert('you have reached your destiny');
}
});
});
here's a quick demo http://jsfiddle.net/8QbwU/
Though Demo doesn't met your full requirements, I believe It does give you some light to proceed further :)
First, you'll want to use jQuery for this
Second, put a placeholder on your page to contain your data.
<table id="dataTable" class="someClass" style="border-collapse: collapse;">
<colgroup>
<col width="12%" />
<col width="12%" />
<col width="12%" />
<!-- Define your column widths -->
</colgroup>
</table>
You'll need to code your own GetData method in a webservice, but this is the general idea (And call Refresh(); from your page load)
function Refresh() {
var getData = function(callback, context, startAt, batchSize) {
MyWebservice.GetData(
startAt, //What record to start at (1 to start)
batchSize, //Results per page
3, //Pages of data
function(result, context, method) {
callback(result, context, method);
},
null,
context
);
};
$('#dataTable').scrolltable(getData);
}
The getData function variable is passed into the scrolltable plugin, it will be called as needed when the table is being scrolled. The callback and context are passed in, and used by the plugin to manage the object you are operating on (context) and the asynchronous nature of the web (callback)
The GetData (note the case) webmethod needs to return a JSON object that contains some critical information, how your server side code does this is up to you, but the object this plugin expects is the following. The Prior and Post data are used to trigger when to load more data, basically, you can scroll through the middle/active page, but when you start seeing data in the prior or post page, we're going to need to fetch more data
return new {
// TotalRows in the ENTIRE result set (if it weren't paged/scrolled)
TotalRows = tableElement.Element("ResultCount").Value,
// The current position we are viewing at
Position = startAt,
// Number of items per "page"
PageSize = tableElement.Element("PageSize").Value,
// Number of pages we are working with (3)
PageCount = tableElement.Element("PageCount").Value,
// Data page prior to active results
PriorData = tbodyTop.Html(),
// Data to display as active results
CurrentData = tbodyCtr.Html(),
// Data to display after active results
PostData = tbodyBot.Html()
};
Next is the plugin itself
/// <reference path="../../js/jquery-1.2.6.js" />
(function($) {
$.fn.scrolltable = function(getDataFunction) {
var setData = function(result, context) {
var timeoutId = context.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
context.data('timeoutId', null);
}
var $table = context.find("table");
var $topSpacer = $table.find('#topSpacer');
var $bottomSpacer = $table.find('#bottomSpacer');
var $newBodyT = $table.children('#bodyT');
var $newBodyC = $table.children('#bodyC');
var $newBodyB = $table.children('#bodyB');
var preScrollTop = context[0].scrollTop;
$newBodyT.html(result.PriorData);
$newBodyC.html(result.CurrentData);
$newBodyB.html(result.PostData);
var rowHeight = $newBodyC.children('tr').height() || 20;
var rowCountT = $newBodyT.children('tr').length;
var rowCountC = $newBodyC.children('tr').length;
var rowCountB = $newBodyB.children('tr').length;
result.Position = parseInt(result.Position);
$newBodyC.data('firstRow', result.Position);
$newBodyC.data('lastRow', (result.Position + rowCountC));
context.data('batchSize', result.PageSize);
context.data('totalRows', result.TotalRows);
var displayedRows = rowCountT + rowCountC + rowCountB;
var rowCountTopSpacer = Math.max(result.Position - rowCountT - 1, 0);
var rowCountBottomSpacer = result.TotalRows - displayedRows - rowCountTopSpacer;
if (rowCountTopSpacer == 0) {
$topSpacer.closest('tbody').hide();
} else {
$topSpacer.closest('tbody').show();
$topSpacer.height(Math.max(rowCountTopSpacer * rowHeight, 0));
}
if (rowCountBottomSpacer == 0) {
$bottomSpacer.closest('tbody').hide();
} else {
$bottomSpacer.closest('tbody').show();
$bottomSpacer.height(Math.max(rowCountBottomSpacer * rowHeight, 0));
}
context[0].scrollTop = preScrollTop; //Maintain Scroll Position as it sometimes was off
};
var onScroll = function(ev) {
var $scrollContainer = $(ev.target);
var $dataTable = $scrollContainer.find('#dataTable');
var $bodyT = $dataTable.children('tbody#bodyT');
var $bodyC = $dataTable.children('tbody#bodyC');
var $bodyB = $dataTable.children('tbody#bodyB');
var rowHeight = $bodyC.children('tr').height();
var currentRow = Math.floor($scrollContainer.scrollTop() / rowHeight);
var displayedRows = Math.floor($scrollContainer.height() / rowHeight);
var batchSize = $scrollContainer.data('batchSize');
var totalRows = $scrollContainer.data('totalRows');
var prevRowCount = $bodyT.children('tr').length;
var currRowCount = $bodyC.children('tr').length;
var postRowCount = $bodyB.children('tr').length;
var doGetData = (
(
(currentRow + displayedRows) < $bodyC.data('firstRow') //Scrolling up
&& (($bodyC.data('firstRow') - prevRowCount) > 1) // ...and data isn't already there
)
||
(
(currentRow > $bodyC.data('lastRow')) //Scrolling down
&& (($bodyC.data('firstRow') + currRowCount + postRowCount) < totalRows) // ...and data isn't already there
)
);
if (doGetData) {
var batchSize = $scrollContainer.data('batchSize');
var timeoutId = $scrollContainer.data('timeoutId');
if (timeoutId) {
clearTimeout(timeoutId);
$scrollContainer.data('timeoutId', null);
}
timeoutId = setTimeout(function() {
getDataFunction(setData, $scrollContainer, currentRow, batchSize);
}, 50);
$scrollContainer.data('timeoutId', timeoutId);
}
};
return this.each(function() {
var $dataTable = $(this);
if (!getDataFunction)
alert('GetDataFunction is Required');
var batchSize = batchSize || 25;
var outerContainerCss = outerContainerCss || {};
var defaultContainerCss = {
overflow: 'auto',
width: '100%',
height: '200px',
position: 'relative'
};
var containerCss = $.extend({}, defaultContainerCss, outerContainerCss);
if (! $dataTable.parent().hasClass('_outerContainer')) {
$dataTable
.wrap('<div class="_outerContainer" />')
.append($('<tbody class="spacer"><tr><td><div id="topSpacer" /></td></tr></tbody>'))
.append($('<tbody id="bodyT" />'))
.append($('<tbody id="bodyC" />'))
.append($('<tbody id="bodyB" />'))
.append($('<tbody class="spacer"><tr><td><div id="bottomSpacer" /></td></tr></tbody>'));
}
var $scrollContainer = $dataTable.parent();
$scrollContainer
.css(containerCss)
.scroll(onScroll);
getDataFunction(setData, $scrollContainer, 1, batchSize);
});
};
})(jQuery);
You'll likely need to tweak some things. I just converted it to a jQuery plugin and it's probably still a little glitchy.

Categories