Update DOM Elements Immediately in Javascript - javascript

For a simple project, I'm trying to display log output in a textarea. I have a log function, and would like the textarea to always be updated immediately whenever log is called. Unfortunately, it seems to only do the update once, and push everything at the same time (I'm using JQuery to set the value of the textarea, if that possibly matters). Here's a basic example:
for (i = 0; i <= 100; i++) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(9999999)
};
This spits out all the text into the textarea at once (note: you can see the results of these examples at this jsfiddle). This basic example is easily remedied by creating a timeout and using recursive function calls:
f = function(i) {
if (i <= 100) {
$("textarea").html($("textarea").html() + "test" + i + "\n");
$("textarea").scrollTop(999999);
setTimeout(function() { f(i+1); }, 0);
}
};
f(1);
This version spits out the text into the textarea one line at a time, which is what I want. But using timeouts and callbacks in this manner does not seem practical in the setting of logging; every time I call log, I would have to provide a callback for all the functionality that I want to ever follow the log call.
Is there any way to achieve the desired effect without callbacks?

I think you might consider using :
$("textarea").val(function(index, value) {
return value + "test" + i + "\n"
});
instead of :
$("textarea").html($("textarea").html() + "test" + i + "\n");
or in general :
$("textarea").val(NEWVAL)
Also noted in the comments of your question, if you want to be able to notice "by eye" all the messages that arrives you'll have to save them in a buffer and have something like (not tested) :
var buffer = []
function log(text) { buffer.push(text) }
setInterval(function(){
if (len(buffer)>0) {
$("textarea").val(function(index, value) {
return value + "\n" + buffer.shift()
});
}
},500)

Browsers generally (Opera is an exception) do JS execution and rendering on the same thread, so while your JS is running there will be no rendering done by th browser, unless you explicitly yield control via a timeout or interval timer.

Related

Prevent JQuery .HTML overwriting previous .HTML before its displayed

I am currently using a multi-dimensional array to store values and then loop round it based on if it meets two requirements does a number match and does it have a set state E.G 487 Online.
This changes in real time based on what event happens to be caught and inserted in the MD array on the server idea and everything is grand on that side of things.
Am trying to visual represent each state of a call in the following format:
If phone device is online turn tile red otherwise turn green and do .HTML.
If phone is ringing turn tile yellow and do .HTML.
If phone is in a call then turn the tile orange and do .HTML.
If phone just hangup then and do .HTML (but it doesn't do this) then turn blue via a css class using .addClass......then remove it after 3 seconds. (It wont display otherwise because status online gets caught right after so you dont even see it turn blue hence using a setTimeout to stop this.)
Steps 1 to 3 work accordingly the issue comes in with step 4.
The issue am having with this is am using JQuery .HTML to overwrite content in the tile every time the phone changes state but after some debugging and fiddling around I have found that the issue seems to be JQuery .HTML and/or how am using it and the setTimeOut I think but I don't know why.
Problem is the Hangup event gets caught........ .HTML here doesn't seem to display and seems to get overwritten by the .HTML in the online event which gets caught straight after hangup and therefore displays .html from the online event instead.
I found when I remove the .HTML at step 1 it stops the issue, but I need it in step 1. But if I leave it there it seems to be overwriting the .HTML in step 4 which I also need for some reason.
Array looks like this with the extension number, SIP device status, current state the device is in e.g Ringing:
[ '487', 'online', 'Hangup' ],
[ '488', 'online' ],
[ '477', 'online', 'Hangup' ] ]
What the server side events generally look like at the point where the event is caught:
477 and 487 both hungup
[ [ '487', 'online', 'Hangup' ], [ '477', 'online', 'Hangup' ] ]
something happened to a ChannelDestroyed
Channel Output ChannelDestroyed
something happened to an endpoint
EndpointStateChange
477 current state is: online
[ [ '487', 'online', 'Hangup' ], [ '477', 'online', 'Hangup' ] ]
Here is my code at the moment:
//Handles events to signal current call states aka if 488 dials 487 highlight both tiles with those values.
socket.on("eventsCalls", function (calldata) {
for (var i = 0; i < calldata.length; i++) {
if (calldata[i][0] && calldata[i][2] === "Ringing") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").css("background-color", "yellow").append("<h4>User</h4><p>" + calldata[i][0] + " Is Ringing</p>");
} else if (calldata[i][0] && calldata[i][2] === "Hangup") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").html("<h4>User</h4><p>" + calldata[i][0] + " just hungup</p>").addClass("hangup");
setTimeout(function () {
$("div[class*='tile']").removeClass("hangup");
}, 3000);
} else if (calldata[i][0] && calldata[i][2] === "ANSWER") {
$("div[class*='tile']:contains('" + calldata[i][0] + "')").css("background-color", "orange").html("<h4>User</h4><p>" + calldata[i][0] + " Is Busy</p>");
}
}
});
// Handles which sip devices are currently registered and displays them on the web page.
socket.on("eventsRegister", function (regisdata) {
for (var i = 0; i < regisdata.length; i++) {
if (regisdata[i][0] && regisdata[i][1] === "online") {
$("div[class*='tile']:contains('" + regisdata[i][0] + "')").css("background-color", "green").html("<h4>User</h4><p>" + regisdata[i][0] + " Online</p>");
} else if (regisdata[i][0] && regisdata[i][1] === "offline") {
$("div[class*='tile']:contains('" + regisdata[i][0] + "')").css("background-color", "red").html("<h4>User</h4><p>" + regisdata[i][0] + " Offline</p>");
}
}
});
Is there any better alternatives or workarounds for this issue am having?
EDIT: JSFiddle for an idea of what am trying to do its hard to replicate the catching of events but when it hits hangup the .html seems to be overwritten by Online event which seems to happen at the same time I think. It doesn't work like that for the last two events on my code the colors change but the .HTML for hangup seems to be overwritten by online.
I can't say for certain, but your setTimeout is missing a delay parameter.
var hangupDelay = 5000; // 5 seconds.
setTimeout(function () {
$("div[class*='tile']").removeClass("hangup");
}, hangupDelay );
This should remove the hangup class after 5 seconds anyway.
If you do not specify the setTimeout delay param; you get it almost instantly:
https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting
delay is the number of milliseconds (thousandths of a second) that the
function call should be delayed by. If omitted, it defaults to 0. The
actual delay may be longer; see Notes below.
This might result in the hangup class instantly being removed and appearing to not be working.

crm2011 plugin call js function

How can I run JavaScript function from appropriate web resource with a plugin in CRM2011.
I couldn't find any information on the Internet. Most of the resources describes how to trigger plugin from JS, but not opposite.
Here's JS code taht is copying the notes to the description field.
When save is clicked. you can see that the data is correctly display in a description field. However if you press save and close and open form again the description field will be empty.
I thought that the reason for that is that the JS executed after safe event but later tests descovered that it's false. Could someone point out an error in this JS code which cause that data is not saving?
Or give a suggestion how's write a plugin which is retrieving data from related entity and writes it into field in the form. thanx
function copyNotes()
{
// CLEAR DESCRIPTION FIELD
alert("JS");
Xrm.Page.getAttribute("description").setValue('');
// GET ID OF THE CASE AND CLEAN IT AND GET URL for oData stuff
//THEN CALL RETRIEVE FUNCTION
var caseID = Xrm.Page.data.entity.getId();
caseID = caseID.replace('{', '').replace('}', '');
var oDataPath = Xrm.Page.context.getServerUrl() + "/xrmservices/2011/organizationdata.svc";
ODataPath = Xrm.Page.context.getServerUrl() + "/XRMServices/2011/OrganizationData.svc";
retrieveRecord(caseID);
}
// CREATE AN HTTP REQUEST AND SEND IT
function retrieveRecord(Id) {
var retrieveReq = new XMLHttpRequest();
retrieveReq.open("GET", ODataPath + "/AnnotationSet?$filter=ObjectId/Id" + " eq (guid'" + Id + "')", true);
retrieveReq.setRequestHeader("Accept", "application/json");
retrieveReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
retrieveReq.onreadystatechange = function() {
// THIS HANDLES A CALLBACK
retrieveReqCallBack(this);
};
retrieveReq.send();
}
function retrieveReqCallBack(retrieveReq) {
if (retrieveReq.readyState == 4 /* complete */ )
{
if (retrieveReq.status == 200) {
//Success
var retrieved = this.parent.JSON.parse(retrieveReq.responseText).d;
// ITERATE THROUGH THE NOTES FOR THIS CASE
for (var i = 0; i < retrieved.results.length; i++) {
// IF IS AN EMPTY FIELD ADD 'case details:'
if (Xrm.Page.getAttribute("description").getValue() == null || Xrm.Page.getAttribute("description").getValue() == 'null') {
Xrm.Page.getAttribute("description").setValue('Case details:');
}
// BOF PARSE DATE
var date = retrieved.results[i].CreatedOn;
date = new Date(parseInt(date.replace("/Date(", "").replace(")/", ""), 10));
// EOF PARSE DATE
var newtext = "--------------------\r\n" + "Created by: " + retrieved.results[i].CreatedBy.Name + " - " + date + "\r\n" + retrieved.results[i].NoteText + "\r\n--------------------\r\n";
var text = Xrm.Page.getAttribute("description").getValue() + "\r\n" + newtext;
Xrm.Page.getAttribute("description").setValue(text);
}
}
}
}
There is no supported way to call back to the client from the server from within the plugin. I'm also not aware of any unsupported way.
I don't think this question even makes sense. Plugin's only trigger when there has been a CRUD operation of some sort. Any CRUD operation triggered by the GUI will result in a refresh of the entity any way. You could perform an update via javascript and an Odata call, but then once the plugin has finished, you can run whatever javascript you'd like to run.
There's no (reasonable) way to do that.
The reason for that is the fact that plugin is a server-size executed thingy. It can't even assume there's a GUI. (Of course, we know there is but generally, a server-size code can't interact with the GUI directly).
JavaScript is client-side code and a client assumes a server. That's (roughly) why JS can call a plugin (although I wouldn't put it that way) but not the other way around.
I've never had a need of such an operation so I'm curious as to what your task is. Are you asking of pure, academic interest or is it a part of a design? Perhaps there's a better way to reach your goal?

IE combo box usage, freezing on selection

I'm using a combo box to select one of four school houses.
Once the selection has been made, I'm using jQuery to run a few functions.
One of the required functions utilises our VLE's own custom APIs. A limitation on the specific API I'm using means that we can only retrieve information for 100 users per call. As such, for a school of 1300, I'm having to run 26 calls (one call for each surname initial).
It works well enough for how often it will be required. I have a loading GIF which holds place until the information is returned.
In FireFox this works as intended, but in Internet Explorer EDIT: VERSION 8 the drop-down simply freezes until the information has been retrieved.
Is there any way to rectify this easily? I don't particularly fancy overhauling the majority of the code - this feature won't be used a huge amount.
widget.onLoad = function(){
HPAnalysisObject.init();
$('select#house_picker').change( function() {
var val = $(this).val();
val = val.split(",");
var label = val[0];
var house_id = val[1];
HPAnalysisObject.initHPTotals( house_id, label );
} );
}
HPAnalysisObject.initHPTotals = function(house_id, label) {
HPAnalysisObject.id_list = [];
$('div#display').html('<img src="/user/74/168586.gif" alt="LOADING..." />');
for (var i = 1; i <= 26; i++) {
initial = String.fromCharCode(64 + i);
Frog.API.get("users.search", {
"params": {"surname": initial, "group": house_id},
"onSuccess": HPAnalysisObject.addUsers
});
}
HPAnalysisObject.setLabel(label);
HPAnalysisObject.getHPTotals();
};
There are additional functions in place, but it's this Frog.API.get call which slows everything down (it makes 26 ajax calls... :).
So, basically, I'm hoping there will be something I can put in place before that call which allows the combo box to return to its un-dropped-down state, and thus show my loading GIF.
Internet Explorer ^^
FireFox ^^
Many thanks.
The Javascript code runs before the UI is allowed to update.
Defer the call to initHPTotals so that the Javascript code executes after the combo box has collapsed, using for example setTimeout. 10 or 20 milliseconds should be fine:
$('select#house_picker').change( function() {
var val = $(this).val();
val = val.split(",");
var label = val[0];
var house_id = val[1];
setTimeout(function() {
HPAnalysisObject.initHPTotals( house_id, label );
}, 10);
} );
Haven't tested this really, but you can try to change HPAnalysisObject.initHPTotals( house_id, label ); to:
setTimeout(function() { HPAnalysisObject.initHPTotals( house_id, label ) }, 100);
This should allow IE to escape the change event.
Also if loading data takes really long then maybe you should change the behavior of the drop down. If some user would make a mistake you would load the data and then load another set. And so you could add a small button near the drop down and load onclick.

How to do Threading in Javascript

So I have a large JSON object i'm returning from the server, then building a datatable from it and displaying it on the form. This usually takes a few seconds.. so I was thinking of a loading bar.
I have the logic behind the loading bar, however the loop that builds the hmtl data is locking down the browser and I cannot call out to the element i need to update.
Here is my function to do this:
function buildDataTable(db_table, container_id) {
var $pb = $("<div id=\"progress-bar\"></div>");
$(container_id).html($pb);
$pb.progressbar({
value: 0
});
$.post("post location", {
view: "all"
}, function (data) {
var headers = "";
var contents = "";
var jsonObject = $.parseJSON(data);
var tik = Math.round(jsonObject.length / 100);
for (key in jsonObject[0]) {
headers += "<th>" + key.replace(" ", " ") + "</th>";
}
for (i in jsonObject) {
contents += "<tr>";
for (j in jsonObject[i]) {
contents += "<td class=\"border-right\">" + jsonObject[i][j] + "</td>";
}
contents += "</tr>";
if(Math.round(i/tik) == i/tik) {
/* if I run the alert (between popups) i can see the progressbar update, otherwise I see no update, the progressbar appears empty then the $(container_id) element is updated with the table i've generated */
alert('');
$pb.progressbar("value",i/tik);
}
}
var html = "<table cellpadding=\"5\" cellspacing=\"0\"><thead><tr>" + headers + "</tr></thead><tbody>" + contents + "</tbody></table>";
$(container_id).html(html);
$(container_id).children("table:first").dataTable({
"bJQueryUI": true,
"sScrollX": "100%"
});
});
}
[Added my comment as an answer]
JavaScript is single threaded. You'll have to break your work up into pieces and call them in sequence using "setTimeout" to allow the GUI to update during processing (in between your calls) but even then the browser will still seem somewhat unresponsive.
You can try using WebWorker: https://developer.mozilla.org/en/DOM/Worker
Thus worker are executed in parallel of the main thread, you cannot exactly achieve multi-threading using workers: you cannot modify the UI from a worker.
You can maybe create your grid as a string in a worker and when the worker finish, append it where you want.
If you do all the building of the Database in a setTimeout, the rest of the page should be responsive.
You can construct the html elements in that function, and when it is ready, attach it to the DOM. You will also have to call functions or send events to update the display of the progress bar.
Edit after comment:
This will run in background and won't affect responsiveness of the page:
window.setTimeout(function() {buildDataTable(db_table, container_id)}, 0);
You already update your progressbar from your function, so this should do it.
However, you might want to decouple the code generating the datatable from the code updating the progressbar.
So it appears the only clean way to do this in my application is to process the json on the server and build the html there. Then return the html to the browser via $.post()
The progress bar will be lost. however I can use a infinite loading gif...

How to tell if an image has loaded correctly

Is there a way to tell, after the fact, whether an image (placed with the <img> tag, not via JS) has loaded correctly into a page? I have a gallery of head shots, and occasionally the third-party image server ends up serving up a 404. I can change the server-side code to use an onerror="showGenericHeadshot()", but I really want to avoid making changes to server-side code. Ultimately, I want to determine if an image is missing or broken and replace it with a generic "Image Not Found" graphic. Things I've tried:
Image.prototype.onerror = showGenericHeadshot -- doesn't work for <img> tags
$('img[src*=thirdpartyserver.com]).error(showGenericHeadshot) -- doesn't work in IE
$('img[src*=thirdpartyserver.com]).css('backgroundImage','url(replacementimage.gif)') -- works, but still doesn't get rid of the broken image icon in IE
<img scr='someUrl' id="testImage" />
jQuery('#testImage').bind('load',function(){
alert ('iamge loaded');
});
to avoid race condition do as below
<img _src="http://www.caregiving.org/intcaregiving/flags/UK.gif" />
// i have added an underscore character before src
jQuery('img').each(function(){
var _elm=jQuery(this);
_elm.bind('load',_imageLoaded).attr('src',_elm.attr('_src'))
});
function _imageLoaded()
{
alert('img loaded');
}
Unfortunately, I'm not able to accept either #TJ Crowder's nor #Praveen's excellent answers, though both do perform the desired image-replacement. #Praveen's answer would require a change to the HTML (in which case I should just hook into the <img> tag's own error="" event attribute. And judging by network activity, it look like if you try to create a new image using the url of an image that just 404ed in the same page, the request actually does get sent a second time. Part of the reason the image server is failing is, at least partly, our traffic; so I really have to do everything I can to keep requests down or the problem will only get worse..
The SO question referred to in #danp's comment to my question actually had the answer for me, though it was not the accepted answer there. I'm able to confirm that it works with IE 7 & 8, FF and webkit browsers. I'm doubtful it will work with older browsers, so I've got a try/catch in there to handle any exceptions. The worse case will be that no image-replacement happens, which is no different from what happens now without doing anything. The implementation I'm using is below:
$(function() {
$('img[src*=images.3rdparty.com]').each(
function() {
try {
if (!this.complete || (!$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0))) {
this.src = 'http://myserver.com/images/no_photo.gif';
}
} catch(e) {}
}
);
});
Would an alternate text be sufficient? If so you can use the alt attribute of the img tag.
I think I've got it: When the DOM is loaded (or even on the window.load event — after all, you want to do this when all images are as complete as they're going to get), you can retroactively check that the images are okay by creating one new img element, hooking its load and error events, and then cycling through grabbing the src from each of your headshots. Something like the code below (live example). That code was just dashed off, it's not production quality — for instance, you'll probably want a timeout after which if you haven't received either load or error, you assume error. (You'll probably have to replace your checker image to handle that reliably.)
This technique assumes that reusing a src does not reload the image, which I think is a fairly reliable assumption (it is certainly an easily testable one) because this technique has been used for precaching images forever.
I've tested the below on Chrome, Firefox, and Opera for Linux as well as IE6 (yes, really) and IE8 for Windows. Worked a treat.
jQuery(function($) {
var imgs, checker, index, start;
// Obviously, adjust this selector to match just your headshots
imgs = $('img');
if (imgs.length > 0) {
// Create the checker, hide it, and append it
checker = $("<img>").hide().appendTo(document.body);
// Hook it up
checker.load(imageLoaded).error(imageFailed);
// Start our loop
index = 0;
display("Verifying");
start = now();
verify();
}
function now() {
return +new Date();
}
function verify() {
if (!imgs || index >= imgs.length) {
display("Done verifying, total time = " + (now() - start) + "ms");
checker.remove();
checker = undefined;
return;
}
checker[0].src = imgs[index].src;
}
function imageLoaded() {
display("Image " + index + " loaded successfully");
++index;
verify();
}
function imageFailed() {
display("Image " + index + " failed");
++index;
verify();
}
function display(msg) {
$("<p>" + now() + ": " + msg + "</p>").appendTo(document.body);
}
});​
Live example

Categories

Resources