jquery load order affects list - ajax

I have the following jQuery to load status to a profile page using a ul with li status items.
The each() takes items from a JSON callback and the load() is supposed to ensure that the image is available before the li is created:
(showpic() gives me a well-formed url to use.)
function showStatus(data){
var jsondata = $.parseJSON(data);
var testText = "";
$('#doNews').empty();
$('#doNews').append($('<ul/>', {"class": "newsList", id: "theNews"}));
$.each(jsondata, function(i, item){
$('<img src="' + showpic(item[3]) + '" class="newsImage">')
.load(function(){
$(this)
.appendTo($('#theNews'))
.wrap($('<li>', {"class": "newsItem"}))
.closest('li')
.append(item[5])
});
});
$("#statustext").val('');
}
the problem is that the status feed now seems to be written to the page in the order the images load. i.e., instead of being written according to the JSON item order, the li s are written in the order of loaded images (this has the effect of grouping status by user, not writing it out by date, as in the JSON).
So...
how would I both write items in the JSON order and still wait for the img to load?
By the way, I looked at this qn:
jQuery each() and load() ordering
and it seems to be on the right track, but when I tried using hide() and then show()inside the load() function, it never seemed to be called, and the img remained hidden. Please give me a simple example if this is the solution you suggest.
Thanks!

Either:
1) Maintain a counter & timeout, count the # of images to load at start, create the items as "hidden", countdown the number of unloaded as they load, then (when counter hits 0 or timer times out) display everything;
or:
2) Create the items as hidden, and show them in the 'load' event. Items will appear in arbitrary order, but will end up being correctly ordered.
Here's a possible example: your code isn't very clear as to what structure is being built & where appended, so this is just a rough (but clearly coded) outline.
Try using intermediate variables more, rather than vast fabulous jQuery constructions, in your own. It will help you debug it.
console.log('loading news items');
$.each( jsondata, function(i, item){
console.log(' item', showpic(item[3]), item[5]);
var img = $('<img src="' + showpic(item[3]) + '" class="newsImage" >');
var element = img.wrap($('<li>', {"class": "newsItem", "style": "display:none;"}))
.closest('li')
.append(item[5]);
element.appendTo( $('#theNews'));
// when the IMG loads, find it's surrounding LI.. and show it.
img.load( function(){
console.log(' loaded', $(this).attr('src'));
$(this).closest('li').show();
});
// put a timer & show it anyway after 8s, if img still hasn't loaded.
element.delay( 8000).show(0);
});
You will also notice logging in the code. Logging is good software practice. Console.log is not available on IE, so you should eventually shim it or use a small library function of your own.
Fundamentally, you have to get away from adding to the DOM inside the 'load' event. That's what's causing the mis-ordering. Add to the DOM in the jsondata each() function, or in a separate well-structured bit of code that guarantees correct ordering.

Related

How do you use the dc.redrawAll() function onclick?

I would like to be able to use the dc.js select menu (dc.selectMenu) in such a way that when I click on an element it gets the value of said element and that becomes the value of the select, once selected it should refresh the data as it normally would if you had just selected in the first place.
The problem I'm having is that I can set the value, but dc.redrawAll() seems to do nothing for me so I think I must be filtering wrongly, but I can't find much information online regarding how to do it other than simply using the filter method (not onclick).
I have tried to set the destination to whatever data-destination is which appears to be working, the value of the select does update when I check with console.log to check the value of the select menu, I then use the dc.redrawAll() function expecting it would filter based on the select option but it does nothing (not even an error in the console)
My function so far is looking like:
function select_destination(ndx) {
var destination_dim = ndx.dimension(dc.pluck('destination'));
var destination_group = destination_dim.group();
var destination = null;
document.addEventListener('click', function(e) {
if (!e.target.matches('.open-popup-link')) return;
e.preventDefault();
var destination = e.target.getAttribute('data-destination').toString();
document.getElementById('select-destination').value = destination;
dc.redrawAll();
});
dc.selectMenu('#select-destination')
.dimension(destination_dim)
.group(destination_group)
.filter(destination);
}
I would expect the graphs to update based on the select option but nothing happens, and I get no error message to go off either.
I suspect I'm using dc.redrawAll() wrongly as if I go to the console and type dc.redrawAll(); I get undefined but I'm really at a loss now and the documentation isn't really helping me at this point so I don't know what else to do.
they are bits of your code that I don't quite understand, for instance why do you have have filter(destination /*=null */)
anyway, So you want to filter the select menu? you can call directly the replaceFilter function with the value, as done in the source code:
menu.replaceFilter(destination);
dc.events.trigger(function () {
menu.redrawGroup();
});
See the source code for the full example of how it's done
https://dc-js.github.io/dc.js/docs/html/select-menu.js.html#sunlight-1-line-129
as for why it doesn't work, I have had some surprising results mixing d3 with pure dom js. Try to rewrite your even handler in d3, eg
d3.select('#select-destination').property('value', destination);
it's possibly that changing the value on the dom directly isn't triggering the change event.
My experience with d3 is that it works better to change the underlying data (call directly filter functions or whatever you want to do) and let dc redraw the needed rather than manipulating the dom directly

How to load images in order with unveil.js

I am using unveil.js to load a site more quickly.
I have a white div that blocks the content, which I want to disappear after the first images have loaded.
I though I could just count the images, but I realize that some of the latter ones could just load first (which normally happens because there is a threshold parameter which loads the ones that follow on the scroll).
Could someone help me with a smart way to do this?
Here's my code and the crappy solution:
$("img").unveil(2000, function() {
$(this).load(function(){
if(imageCount >= 4){
$(".white-cover").fadeOut("slow");
imageCount = 0;
}
Since unveil changes the src attribute, you can use jQuery to listen to an attribute change which indicate that an image has been replaced.
$(document).ready(function(){
$("#selected-date-range").change(function(){
alert( $("#selected-date-range").attr("value") );
});
});
In case I understand your problem correctly, I would use:
$(document).ready(function(){
// all images are loaded
}
Does this help you?

Call function when "on scroll" event is triggered

I have a grid that is locally initialized from an array data. after adding the rows to it I want to be able to get the event of the scrollbar when it reaches to the end and load more rows locally.
(the grid has certain height and 'overflow-y' : scroll)
How can this be done?
Thanks In Advance.
You can see the code for loading more data at line 1831 of grid.base.js:
case "local":
case "clientside":
beginReq();
ts.p.datatype = "local";
var req = addLocalData();
addJSONData(req,ts.grid.bDiv,rcnt,npage>1,adjust);
$(ts).triggerHandler("jqGridLoadComplete", [req]);
if(lc) { lc.call(ts,req); }
$(ts).triggerHandler("jqGridAfterLoadComplete", [req]);
if (pvis) { ts.grid.populateVisible(); }
endReq();
The good news is that the loadComplete event is called, so you can put your code there. The only complication is that loadComplete may also be called when the grid is initially constructed, so you have to take that into account as well.

starting smoothdivscroll at random position

I am using smoothdivscroll plugins. I want to start scrolling at different position f text when page is reloaded or refreshed. How can I do this?
Thanks.
You would have to write some code yourself for this special function. SmoothDivScroll has an option, startAtElementId, that you can use to tell the scroller to start at the position of a certain element (id). If you are loading the content of the scroller dynamically on the server side (using PHP, ASP.NET or something similar) you could make the server side code pick one of the content elements and add a certain id to it. Then you would set the option like this:
<script type="text/javascript">
$(document).ready(function() {
$("#makeMeScrollable").smoothDivScroll({
startAtElementId: "yourID"
});
});
</script>
You could also use the method jumpToElement. First you would initialize the plugin. Then directly after you would make a call to jumpToElement and tell it to jump to an element with a random number between 1 and the total number of elements you have loaded. This is not tested in any way, but I think it would look something like this:
<script type="text/javascript">
$(document).ready(function() {
// Initialize the plugin
$("#makeMeScrollable").smoothDivScroll();
// Randomize an element
var numberOfElements = $("#scrollableArea *").length;
var randomNumber = Math.floor(Math.random() * numberOfElements + 1 );
$("#makeMeScrollable").smoothDivScroll("jumpToElement", "number", randomNumber);
});
</script>
As I said, the above code has not been tested, but something along those lines should work.
Good luck!

Is there a way to break out of event recursion in jQuery without using a global variable?

I have a form with 2 text inputs and 2 span controls. Normally, when textbox A is changed an event is fired to change span A, and when textbox B is changed, an event is fired to change span B.
However, in one particualar case I would like a change either textbox A or textbox B to update both span A and B. I tried wiring the events up to the corresponding controls in this case, but it didn't work because there is much state that is set up in the event building code (not to mention each event calls 'this', which would make the logic use the wrong control if it were fired from a different one than it was intended).
To make things easy, it would be best to pass a string (representing the other text input id) to the event handler at the time it is created, and then calling the change() event manually on the second control. However, this puts things in an infinite loop of recursion. I thought of a way to get around the recursion, but it reqires a global variable.
Is there a better way than this, preferably one that doesn't require a global variable?
ml_inEvent = false;
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event) {
// Other processing happens here...
if (event.data.controlToFire != '') {
var $controlToFire = $('#' + event.data.controlToFire);
if ($controlToFire.length) {
// Use a global variable to ensure this event is not fired again
// as a result of calling the other one
if (!ml_inEvent) {
ml_inEvent = true;
$controlToFire.change();
ml_inEvent = false;
}
}
}
});
You can use the extraParameters argument on .trigger() to break out, for example:
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event, fire) {
// Other processing happens here...
if(fire !== false) $('#' + event.data.controlToFire).trigger('change', false);
});
You can give it a try here. What this does is the event handler callback not only receives the event object but also any other arguments you pass in, in this case we're just using a simple false and a !=== check this in important so undefined (the parameter not passed at all) still changes both controls. So for instance $("#control").change() would change both controls, but still not loop...you can test that here.

Resources