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.
Related
I wrote a simple website, which displays 1 of 150 Psalms at random with a random image from a small array of images. Once loaded user gets two choices, either read another Psalm at random (button "Any Psalm") or choose one themselves (select element). The function that loads a random Psalm works fine, but the select button, though works fine on local server and also on my laptop after pushing it to Github, but on my phone and iPad it works only if you select again, so say you're on Psalm 20, then you select Psalm 83 and then nothing happens, until you select again, say psalm 102, only then that it loads psalm 83, so you're always one step behind.
I suspect it may have nothing to do with the code, nonetheless, here's my function;
const selectPsalm = () => {
loadingImage();
let psalms = randomPsalm(psalmsBulk)[1];
let selectedPsalmIndex = document.getElementById("choose").value;
let lastIndex = () => {
if(psalms.indexOf(psalms[selectedPsalmIndex])<10) {
return 2
} else if(psalms.indexOf(psalms[selectedPsalmIndex])<100) {
return 3
} else return 4
}
strHeading = psalms[selectedPsalmIndex].slice(0, psalms[selectedPsalmIndex].indexOf(psalms.indexOf(psalms[selectedPsalmIndex]))+lastIndex());
strScripture = psalms[selectedPsalmIndex].slice(psalms[selectedPsalmIndex].indexOf(psalms.indexOf(psalms[selectedPsalmIndex]))+lastIndex());
document.getElementById('heading').innerHTML = strHeading;
document.getElementById('psalm').innerHTML = strScripture;
}```
and these are my event listeners:
//document.getElementById("random").addEventListener('click', newRandomPsalm);
//document.getElementById("choose").addEventListener('click', selectPsalm);
$('document').ready(function(){
$('#random').on('click', newRandomPsalm);
$('#choose').on('click', selectPsalm)
})
I commented out my original js event listeners and changed them for jQuery ones, because I am learning jQuery and wanted to see it in my code, but both sets of statements do exactly the same.
You can see it in action at:
https://psalms.live/
My question is; what is wrong and how to fix it?
it turns out that on('click') is not always suitable for select dropdown element. The correct syntax is on('change').
I'm trying to build a spreadsheet based around DataDT's excellent API for 1-minute Forex data. I'm trying to build a function that 1) Reads a value ("Date time") from a cell 2) Searches for that value in a given URL from the aforementioned API 3) Prints 2 other properties (open & close price) for that same date.
In other words, It would take input from rows N and O, and output the relevant values (OPEN and CLOSE from the API) in rows H and I.
(Link to current GSpreadsheet)
This spreadsheet would link macroeconomic news and historic prices and possibly reveal useful insights for Forex users.
I already managed to query data from the API effectively but I can't find a way to filter only for the datetimes I'm asking. Much less iterating for different dates! With the help from user #Cooper I got the following code that can query entire pages from the API but can't efficiently filter yet. I'd appreciate any help that you might provide.
This is the current status of the code in Appscript:
(Code.gs)
function searchOnEdit(e) {
//e.source.toast('Entry');// I use these lines for debugging
var sh=e.range.getSheet();
if(sh.getName()!='API') return;
var checkedValue='TRUE';//these are the defaults if you install the checkboxes from the Insert Menu
var uncheckedValue='FALSE';
if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
e.range.setValue(uncheckedValue);//this was commented out it should not have been sorry for that Cooper
//e.source.toast('flag1');
var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
var obj={datetime:r[14],url:r[13],event:e};
var dObj=getDataDT1(obj);
//Logger.log(dObj);
sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);//loading OPEN on your spreadsheet
sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);//loadding CLOSE on your spreadsheet
}
}
//{datetime:'',url:'',event:e}
function getDataDT1(obj) {
Logger.log(JSON.stringify(obj));//I need to see this
var r=UrlFetchApp.fetch(obj.url);
var data=JSON.parse(r.getContentText("UTF-8"));
//Logger.log(data);
var pair='USDJPY';
var dat=new Date(obj.datetime);
var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
for(var i=0;i<data.length;i++) {
var dt=data[i].DATE_TIME.split(' ');
var sd=new Date(data[i].DATE_TIME);
var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
if(sdv==dtv) {
var d=dt[0].split('-');
var t=dt[1].split(':');
var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
break;
}
}
//Logger.log(rObj);
return rObj;
}
(Appscript.json)
{
"timeZone": "America/Caracas",
"dependencies": {
},
"exceptionLogging": "STACKDRIVER",
"oauthScopes": ["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/script.scriptapp","https://www.googleapis.com/auth/spreadsheets"]
}
(Trigger.js)
function createSearchOnEditTrigger() {
var ss=SpreadsheetApp.getActive();
ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}
onEdit Search
You will need to add a column of checkboxes to column 17 and also create an installable onEdit trigger. You may use the code provided or do it manually via the Edit/Project Triggers menu. When using the trigger creation code please check to insure that only one trigger was creates as multiple triggers can cause problems.
Also, don't make the mistake of naming your installable trigger onEdit(e) because it will respond to the simple trigger and the installable trigger causing problems.
I have an animation below showing you how it operates and also you can see the spreadsheet layout as well. Please notice the hidden columns. I had to do that to make the animation as small as possible. But I didn't delete any of your columns.
It's best to wait for the the check box to get reset back to off before checking another check box. It is possible to check them so fast that script can't keep up and some searches may be missed.
I also had to add these scopes manually:
"oauthScopes":["https://www.googleapis.com/auth/userinfo.email","https://www.googleapis.com/auth/script.external_request","https://www.googleapis.com/auth/spreadsheets"]
You can put them into your appsscript.json file which is viewable using the View/Show Manifest File. Here's a reference that just barely shows you what they look like. But the basic idea is to put a comma after the last entry before the closing bracket and add the needed lines.
After you have created the trigger it's better to go into View/Current Project triggers and set the Notifications to Immediate. If you get scoping errors it will tell you which ones to add. You add them and then run a function and you can reauthorize the access with the additional scopes. You can even run a null function like function dummy(){};.
This is the onEdit function:
function searchOnEdit(e) {
//e.source.toast('Entry');// I use these lines for debugging
var sh=e.range.getSheet();
if(sh.getName()!='API') return;
var checkedValue='TRUE';//these are the defaults if you install the checkboxes from the Insert Menu
var uncheckedValue='FALSE';
if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
e.range.setValue(uncheckedValue);
//e.source.toast('flag1');
var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
var obj={datetime:r[14],url:r[13],event:e};//you dont really need e here
var dObj=getDataDT1(obj);
//Logger.log(dObj);
sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);//loading OPEN on your spreadsheet
sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);//loadding CLOSE on your spreadsheet
}
}
This is the search function. I tried caching the data but it was too large. So if you can reduce the size significantly that would help speed things up for consecutive searches.
//{datetime:'',url:'',event:e}
function getDataDT1(obj) {
var r=UrlFetchApp.fetch(obj.url);
var data=JSON.parse(r.getContentText("UTF-8"));
//Logger.log(data);
var pair='USDJPY';
var dat=new Date(obj.datetime);
var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
for(var i=0;i<data.length;i++) {
var dt=data[i].DATE_TIME.split(' ');
var sd=new Date(data[i].DATE_TIME);
var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
if(sdv==dtv) {
var d=dt[0].split('-');
var t=dt[1].split(':');
var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
break;
}
}
//Logger.log(rObj);
return rObj;
}
This is the create Trigger function. Becareful not to run this more than once and always go check to see that there is only one and set the notifications to immediate when you first turn it on so you'll get emails pretty quickly after errors occur.
function createSearchOnEditTrigger() {
var ss=SpreadsheetApp.getActive();
ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}
Animation:
This is a copy of your spreadsheet with the check boxes.
If you have any difficulties feel free to come back and get some help. I know some of this stuff sounds a bit daunting but it's kind of like going metric. Once you've measured and weighed a few things it begins to sound and feel natural.
Here's my code exactly as I have it in my script which is running right now. Perhaps I have a typo in it due to editing in comments. This has some debugging lines in which run the Logger and display toasts which you should probably comment out.
function searchOnEdit(e) {
e.source.toast('Entry');
var sh=e.range.getSheet();
if(sh.getName()!='API') return;
var checkedValue='TRUE';
var uncheckedValue='FALSE';
if(e.range.columnStart==17 && e.range.rowStart>1 && e.value==checkedValue) {
e.range.setValue(uncheckedValue);
e.source.toast('flag1');
var r=sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).getValues()[0];
var obj={datetime:r[14],url:r[13],event:e};
var dObj=getDataDT1(obj);
Logger.log(dObj);
sh.getRange(e.range.rowStart,4).setValue(dObj.OPEN);
sh.getRange(e.range.rowStart,5).setValue(dObj.CLOSE);
}
}
//{datetime:'',url:''}
function getDataDT1(obj) {
var r=UrlFetchApp.fetch(obj.url);
var data=JSON.parse(r.getContentText("UTF-8"));
//Logger.log(data);
var pair='USDJPY';
var dat=new Date(obj.datetime);
var dtv=new Date(dat.getFullYear(),dat.getMonth(),dat.getDate(),dat.getHours(),dat.getMinutes()).valueOf();
for(var i=0;i<data.length;i++) {
var dt=data[i].DATE_TIME.split(' ');
var sd=new Date(data[i].DATE_TIME);
var sdv=new Date(sd.getFullYear(),sd.getMonth(),sd.getDate(),sd.getHours(),sd.getMinutes()).valueOf();
if(sdv==dtv) {
var d=dt[0].split('-');
var t=dt[1].split(':');
var datestring=Utilities.formatString('%s/%s/%s',d[1],d[2],d[0]);
var timestring=Utilities.formatString('%s:%s',t[0],t[1]);
var rObj={DATE:datestring,TIME:timestring,PAIR:pair,OPEN:data[i].OPEN.toFixed(3),CLOSE:data[i].CLOSE.toFixed(3)};
break;
}
}
//Logger.log(rObj);
return rObj;
}
function createSearchOnEditTrigger() {
var ss=SpreadsheetApp.getActive();
ScriptApp.newTrigger('searchOnEdit').forSpreadsheet(ss.getId()).onEdit().create();
}
I'm using Anki to help my 5th grade son study Math. My son has reached a point where he needs to review how to approach specific problems and not simply memorize facts.
For example imagine you are trying to learn how to multiply fractions...
.45 X .61 = ?
I needed a way to put random math questions into an Anki card. This means that one day the card would display
.45 X .61 = ?
The next time the card is displayed the question might be..
.34 X .12 = ?
After scouring the internet I was able to find an article that explains how to do this...
https://yingtongli.me/blog/2015/03/15/random-question-generator-on-anki-using.html
I'm not a JavaScript developer, but I was able to get this technique to work after banging my head on my keyboard for a week.
Let me explain how you do this and then I'll explain where the problem is.
Install the 2.0 version of Anki.
Open Anki and create a new deck.
Add a new card to the deck.
The add card dialog box appears. At the very top are two long buttons... "Type" & "Deck". Click on the "Type" button to the left.
The Choose note type dialog box appears.
Click on the "Manage" button at the bottom of the dialog box.
Select the Basic note type and click the "Add" button to the left.
The first option should be "Add Basic". Click the "ok" button at the bottom of the dialog box.
Name your new note type.. "Scripting Note Type" then click "OK"
Hit escape.
Now select your new note type ("Scripting Note Type") then click "Choose".
Now we want to add an additional field. By default this note type has fields named "Front" & "Back". We want to add a third field named "Script".
There is a button labeled "Fields" near the top left. Click on this button.
Another dialog box appears. To the left is an Add button. Click it.
Enter Script in the dialog box that appears and click the "OK" button.
Now close the current dialog box by clicking the close button near the button toward the right side.
Let set up our cards now. Near the top left of the current screen are two buttons. The button to the right is labeled "Cards". Click this button.
This is where we set up the card template. One part of the JavaScript code goes into the Front Template. There should be the following text here currently...
{{Front}}
Delete everything and paste the following code.
<script>
function persist(cb) {
window.setTimeout(function() {
// Determine whether to use Anki's Bridge object (Desktop) or sessionStorage (AnkiDroid) to store data across sides.
var dummy = {};
var mode = "dummy";
if (typeof(py) !== "undefined") {
mode = "py";
py.data = py.data || {};
} else if (typeof(sessionStorage) !== "undefined") {
mode = "sessionStorage";
}
var dataObj = {
setItem: function(key, val) {
if (mode === "dummy") {
dummy[key] = val;
} else if (mode === "py") {
py.data[key] = val;
} else if (mode === "sessionStorage") {
sessionStorage.setItem(key, val);
}
},
getItem: function(key, def) {
var val = undefined;
if (mode === "dummy") {
val = dummy[key];
} else if (mode === "py") {
val = py.data[key];
} else if (mode === "sessionStorage") {
val = sessionStorage.getItem(key);
}
if (val == null) {
return def;
} else {
return val;
}
},
tryItem: function(key, val) {
var currVal = dataObj.getItem(key, undefined);
if (currVal == null) {
dataObj.setItem(key, val);
return val;
} else {
return currVal;
}
},
clear: function() {
if (mode === "dummy") {
dummy = {};
} else if (mode === "py") {
window.py.data = {};
} else if (mode === "sessionStorage") {
sessionStorage.clear();
}
}
};
if (!document.getElementById("back")) {
dataObj.clear();
}
cb(dataObj);
}, 0); //Execute after Anki has loaded its Bridge object.
}
</script>
<script>
var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending comments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); //Replace special symbols.
eval(code);
</script>
<div id="front">{{Front}}</div>
This JavaScript takes the JavaScrippt code that we will place in the "Script" field and executes it with an eval statement.
Notice in this code the persist function that keeps values consistent between the front and back of the Anki card. My issue is related to this persist function.
In the bottom third of the screen is a "Back Template". Delete everything and paste the following code...
{{FrontSide}}
<hr id=answer>
<div id="back">{{Back}}</div>
On the lower right click the "Close" button.
Now we need to fill in the fields. The values in front and back do not matter. I put "Front" in Front and "Back" in back.
For the Script field we want to add some code. We need to open the HTML view before we paste code. Make sure your cursor is in the Script fields.
In the 2.0 version of Anki there is a button with a picture of a down arrow in the upper right portion of the screen. The 2.1 version has "..." instead of the down arrow button.
Click this arrow and a menu appears. Select the "Edit HTML" menu option.
Paste the following code...
window.setTimeout(function(){
persist(function(data) {
var num1 = data.tryItem("num1",Math.random());
document.getElementById("front").innerHTML = num1;
document.getElementById("back").innerHTML = num1;
});
},0);
This works in the 2.0 version of Anki.
Here is the Question
And here is the Answer...
We generated a random number in JavaScript and that value is persisted between the front and back of an Anki Card. This opens up worlds of possibility for Math and Science Anki cards using random numbers.
However, all is not well in Whoville.
When you run this code on the 2.1 version of Anki it does not work.
The script is bypassed and the static values of the Front and Back fields are displayed.
JavaScript is still being executed. I could put an alert statement in the Script field and it works.
The problem is in the Persist function. The newer version of Anki does not like something about the Persist function.
Can anyone point me in the right direction.
I figured it out...
The issue is that Anki 2.1 eliminated the ability to store persistent data in the window.py object.
Now you might be asking (as I did) what is the window.py object? I'm not 100% sure but I believe its an internal Python object that allows some interactions between JavaScript and Python. Anki is written in Python.
In any case window.py is no longer accessible in Anki 2.1 which mean the technique I described in my question (see above) no longer works in Anki 2.1.
Fortunately, the author the article (referenced above in the question) mentioned that the window object is shared across invocations of an Anki card question and answer.
I started playing around with the window object in Javascript and I was able to get data persistence working in Anki 2.1 (This also works in Anki 2.0)
In the question above I go into great depth into to this technique to generate persistent random values in your Anki cards. This means you generate a random number in the question and that same values is available in the answer. I won't go into as much detail here, but refer the the question above for more details.
In the "Front Template" here is the code.
<script>
var code = (function () {/* {{Script}} */}).toString();
code = code.replace(/^[^\/]+\/\*!?/, "").replace(/\*\/[^\/]+$/, ""); //Strip beginning/ending comments.
code = code.replace(/<div>/g, "\n").replace(/<\/div>/g, "\n").replace(/<br \/>/g, "\n"); //Strip HTML.
code = code.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&"); //Replace special symbols.
eval(code);
</script>
<div id="front">{{Front}}</div>
This is the same code from the article. I have simply removed some code that no longer works.
Here is the card script code...
window.setTimeout(function(){
var windowsref = window; //Set a reference to the window object
if ((("num1" in windowsref) == false) || windowsref.cnt != 1) {
windowsref.num1 = Math.random();
windowsref.cnt = 0;
}
windowsref.cnt++;
document.getElementById("front").innerHTML = windowsref.num1;
document.getElementById("back").innerHTML = windowsref.num1;
},0);
This code works only in Anki 2.1 desktop. I also tested iOS and it works there as well.
I hope this helps someone else.
check out
https://github.com/SimonLammer/anki-persistence
It uses different approaches to achieve persistence.
But Anki environment is somewhat unpredictable, and you need to check if the code has succesfuly initialized.
glad to hear that you found a solution, codingguy.
For further information, I want to add
I have dabbled with the same idea around 2014.
I got it to work using JavaScript and Cookies in my note templates.
For that, I had to modify Anki with a plugin (to monkey-patch creation of the Qt Web View) which I shared as Anki 2.0 plugin JS Booster.
Adapted for Anki 2.1, another developer published the same solution here Anki 2.1 plugin Cookie Monster.
I have gone through many stackoverflow posts and other websites to find a solution to this problem, but none of the solutions i could find either fit my problem or just straight up didn't work.
Im using javascript and jQuery (and some Java for background work) to build a page on a website. The page contains a table with data (the data is handled by Java) and i want to have that table refresh every 10 seconds. That works.
But now i also want to highlight all the cells that have changed values in them. For that, as you see in my code snippet, i just simply turn the background of those cells black. That works too.
My problem is that the color only changes for a split second before changing back to standard white. Using the console and playing around a bit i was able to find out that the table is actually reloaded twice, which to me must mean the load-command is executed twice.
window.setInterval(function () {
if (frozen == false) {
$('table').load(document.URL + ' table');
var elem = document.getElementById("freezebtn"); //This is a button generated by Java Code
elem.style.background = getRandomColor();
var tab = document.getElementsByTagName('table').item(1);
var l = tab.rows.length;
for (var i = 0; i<l; i++) {
var tr = tab.rows[i];
var cll = tr.cells[1];
var check = tr.cells[0];
if(check.innerText === "getAsk") {
var valAsk = cll.innerText;
var ask = Number(valAsk);
if (ask != loadPreviousAsk()) {
console.log("TELEFONMAST!");
cll.style.backgroundColor = "#000000";
}
}
//Do this for every other Updateable Cell
...
}
cachePreviousValues(someValues,thatINeed,To,BeCached);
}
My Question is, what is/could be causing the repeat of the load command (or why won't the cells stay black at all)?
Is there a better way of achieving what im trying to do?
Could my code sample be optimized in any other way?
$.fn.load() is async (by default). To handle any logic regarding loaded data, you can use the complete callback:
$('table').load(document.URL + ' table', function(){/* your logic regarding loaded data here*/);
Right now im trying to find a way to detect when an elements HTML has changed.
I'm currently trying:
var a, b;
setInterval(function() {
a = $('#chat').text();
}, 150);
setInterval(function() {
b = $('#chat').text();
if (a !== b) {
alert("There has been a new message.");
}
}, 200);
What I do is every 150 milliseconds I check for the HTML of #chat and then every 200 seconds I check the HTML again and then check if variable a does not equal to variable b them in the future I will so something with that but for right now I just alert something.
You can see it live here: http://jsfiddle.net/MT47W/
Obviously this way is not working and is not very accurate at all.
Is there a better/different to do/achieve this?
Thanks for any help, I've been trying to figure out how to do this a better for about a week now but I just can't find a fix for this and i'm hoping I posted this problem at the right place, and at the right time.
Use a var to store the element's current text then check against it in a setInverval and update the var to store the current text after checking:
var a = $('#chat').text();
setInterval(function() {
if (a !== $('#chat').text()) { //checks the stored text against the current
alert("There has been a new message."); //do your stuff
}
a = $('#chat').text(); //updates the global var to store the current text
}, 150); //define your interval time, every 0.15 seconds in this case
Fiddle
You may as well store the value in the .data() of the element to avoid using globals.
Example using .data():
$('#chat').data('curr_text', $('#chat').text());
setInterval(function() {
if ($('#chat').data('curr_text') !== $('#chat').text()) {
alert("There has been a new message.");
}
$('#chat').data('curr_text', $('#chat').text());
}, 150);
Fiddle
Another approach, to save client's memory, you can just store the number of child divs your #chat element has:
$('#chat').data('n_msgs', $('#chat').children().length);
setInterval(function() {
if ($('#chat').data('n_msgs') !== $('#chat').children().length) {
alert("There has been a new message.");
}
$('#chat').data('n_msgs', $('#chat').children().length);
}, 150);
Fiddle
EDIT: Here's my very final addition, with a DOM mutation event listener:
$('#chat').on('DOMNodeInserted', function() {
alert("There has been a new message.");
});
Fiddle (not tested in IE < 8)
Note: As noted in the comments, even though mutation events are still supported they're classed as deprecated by W3C due to the performance loss and some incompatibilities across different browsers, therefore it's suggested to use one of the solutions above and only use DOM Mutation events when there's no other way around.
Just checking the last chat would improve efficiency and also do what you want. The only way that it would not work is if the same person sends the same message twice - which most likely will not happen.
I hope this would work:
var lastMessage = $('#chat .body').last().text();
function checkMessages(){
var newLastMessage = $('#chat .body').last().text();
if(lastMessage !== newLastMessage && $('#chat .body').last().length > 0){
//message has changed
alert("There has been a new message.");
lastMessage = $('#chat .body').last().text();
}
setTimeout(function(){checkMessages();},1000);
}
checkMessages();
Js Fiddle
Use crc32 or md5 hashing to check whether data has changed. Just get the html content of the div that you want to check, hash it as a string with either crc32 or md5 and you'll get a string that will represent that content. Use a hidden timestamp etc to be sure that multiple messages by one user get a different hash. If you do this in a setInterval callback, you should be good.
Although it is not highly recommended, it is also possible to use Paul Irish's Duck punching method to tie into jQuery's append function - if you know for sure that the append function is how content is being added (demo):
(function($) {
// store original reference to the method
var _old = $.fn.append;
$.fn.append = function(arg) {
// append as usual
_old.apply(this, arguments);
// do your own checking here
// "this" is a jQuery object
if (this[0].id === "chat") {
alert('there is a new message!');
}
return this;
};
})(jQuery);
With this method, no timing loops are needed.