I wrote the following code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JSON On-change Sample</title>
</head>
<body>
<style>
span {
font-weight: bold;
}
</style>
<div id="placeholder"></div>
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
$(document).ready(function(){
//DOCUMENT READY FUNCTION
loadData();
});
function loadData() {
$.getJSON('data.json', function(data) {
var output="<span>";
output+= data.response.items[0].state;
output+= data.response.items[1].state;
output+="</span>";
document.getElementById("placeholder").innerHTML=output;
var initial = (data.response.items[0].state);
var initialSecond = (data.response.items[1].state);
if (initial>0 || initialSecond>0) {
document.title= 'State 1:' + " " + initial + " " + "and" + " " + "State 2:" + " " + initialSecond;
}
$('#placeholder').change(function() {
alert("Changed!");
});
});
setTimeout("loadData()",800);
}
</script>
</body>
</html>
It's purpose is to simply update every 800 milliseconds and update the results on the page. It works.
The problem is that I want a simple alert whenever either of the state of the two items changes, saying which product ID change from which state to which state.
I can't even seem to be able to get the alert first, not even mentioning telling it which product changed and from what state to what state.
Why isn't my alert working? The jQuery documentation state that this... this should be functional.
EDIT:
Solved with the help of blunderboy's solution below.
Here's how it ended-up looking:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>JSON On-change Sample</title>
</head>
<body>
<style>
span {
font-weight: bold;
}
#placeholderOne, #placeholderTwo {
font-weight: bold;
margin-bottom: 15px;
text-align: center;
color: #b20000;
}
h3, h4 {
margin-bottom: 15px;
text-align: center;
}
</style>
<h3>The numbers represent the state of the two products. Get the alert and the update via AJAX by changing the state data in the data.json file and saving it.</h3>
<h4> Product 1: </h4>
<div id="placeholderOne"></div>
<h4> Product 2: </h4>
<div id="placeholderTwo"></div>
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script>
$(document).ready(function(){
//DOCUMENT READY FUNCTION
loadData();
});
function loadData() {
$.getJSON('data.json', function(data) {
var output = data.response.items[0].state;
var outputTwo = data.response.items[1].state;
// Cache the initialState before putting updated value in it
var initialStateOne = document.getElementById("placeholderOne").innerHTML;
var initialStateTwo = document.getElementById("placeholderTwo").innerHTML;
document.getElementById("placeholderOne").innerHTML=output;
document.getElementById("placeholderTwo").innerHTML=outputTwo;
var initial = (data.response.items[0].state);
var initialSecond = (data.response.items[1].state);
if (initial > 0 || initialSecond > 0) {
document.title= 'State 1:' + " " + initial + " " + "and" + " " + "State 2:" + " " + initialSecond;
}
if (initialStateOne != output && initialStateOne > 0) {
alert("Product ID " + data.response.items[0].product_id + " state changed from " + initialStateOne + " to " + data.response.items[0].state);
if (initialStateTwo != outputTwo && initialStateTwo > 0) {
alert("Product ID " + data.response.items[1].product_id + " state changed from " + initialStateTwo + " to " + data.response.items[1].state);
}
};
});
setTimeout("loadData()",800);
}
</script>
</body>
</html>
Check out jQuery Change Event API. It clearly says change event is limited to textarea, select and input. That's why you can not get this working.
Instead you could compare previous and the latest values manually and alert your change event.
Your code will look like this:
function loadData() {
$.getJSON('data.json', function(data) {
var output="<span>";
output+= data.response.items[0].state;
output+= data.response.items[1].state;
output+="</span>";
// Cache the initialState before putting updated value in it
var initialState = document.getElementById("placeholder").innerHTML;
document.getElementById("placeholder").innerHTML=output;
var initial = (data.response.items[0].state);
var initialSecond = (data.response.items[1].state);
if (initial > 0 || initialSecond > 0) {
document.title= 'State 1:' + " " + initial + " " + "and" + " " + "State 2:" + " " + initialSecond;
}
if (initialState != output) { //See this is the only change
alert("Changed!");
};
});
setTimeout("loadData()",800);
Here is another answer to your problem. You can use DOM Mutation Event: DOMSubtreeModified to figure out the changes in the subtree of an element. Here is JSbin Demo
HTML
<div id='a'></div>
JS
var $a = $('#a');
$a.on('DOMSubtreeModified', function() {
alert('Changed');
});
$a.html('This is test1');
$a.html('This is test2');
PS: Please note that this method will also call the event handler when you change any element inside this div. So you better remove this event handler when no longer required or handle such cases gracefully.
Related
We have a requirement to track and audit production, essentially we have lots of orders of which we seem to be loosing some of our products on the way (spoils etc).
To stop this we have now placed orders on to google sheets and listed a quantity of how many there should be, an employee then would write in how many was recieved.
On the web app I want to show the table and have a field where the employee can enter the count,
So if I had a google sheet I might have in column A [Product Name] column b [Start Quanity] and in column C [Counted Quantity]
On the web app it will show all 3 columns but allow the user to enter the count into the [Counted Quantity] column.
Is this possible? (Assuming I either need a input field within the html table or an editable table?
Thanks
This will get all of the data in your spread but you can only edit the last column. It's built as a dialog but one could easily change that to a webapp.
I had some problems with scopes so I thought you might need them. If you don't know what do to with these then skip them and come back if you have a problem.
"oauthScopes": ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/script.external_request", "https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/script.container.ui"]
ordercounts.gs:
function getOrders() {
var ss=SpreadsheetApp.getActive();
var osh=ss.getSheetByName("Orders");
var org=osh.getDataRange();
var vA=org.getValues();
var rObj={};
var html='<style>th,td{border: 1px solid black}</style><table>';
vA.forEach(function(r,i){
html+='<tr>';
r.forEach(function(c,j){
if(i==0){
html+=Utilities.formatString('<th>%s</th>',c);
}else if(j<r.length-1){
html+=Utilities.formatString('<td>%s</td>', c);
}else{
html+='<td id="cell' + i + j + '">' + '<input id="txt' + i + j + '" type="text" value="' + c + '" size="20" onChange="updateSS(' + i + ',' + j + ');" />' + '</th>';
}
});
html+='</tr>';
});
html+='</table>';
return html;
}
function updateSpreadsheet(updObj) {
var i=updObj.rowIndex;
var j=updObj.colIndex;
var value=updObj.value;
var ss=SpreadsheetApp.getActive();
var sht=ss.getSheetByName("Orders");
var rng=sht.getDataRange();
var rngA=rng.getValues();
rngA[i][j]=value;
rng.setValues(rngA);
var data = {'message':'Cell[' + Number(i + 1) + '][' + Number(j + 1) + '] Has been updated', 'ridx': i, 'cidx': j};
return data;
}
function launchOrdersDialog(){
var userInterface=HtmlService.createHtmlOutputFromFile('orders');
SpreadsheetApp.getUi().showModelessDialog(userInterface, "OrderCounts");
}
orders.html:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script>
$(function(){
google.script.run
.withSuccessHandler(function(hl){
console.log('Getting a Return');
$('#orders').html(hl);
})
.getOrders();
});
function updateSS(i,j) {
var str='#txt' + String(i) + String(j);
var value=$(str).val();
var updObj={rowIndex:i,colIndex:j,value:value};
$(str).css('background-color','#ffff00');
google.script.run
.withSuccessHandler(updateSheet)
.updateSpreadsheet(updObj);
}
function updateSheet(data) {
//$('#success').text(data.message);
$('#txt' + data.ridx + data.cidx).css('background-color','#ffffff');
}
console.log('My Code');
</script>
</head>
<body>
<div id="orders"></div>
</body>
</html>
Here's what the tool looks like:
All you have to do is enter the updated count and hit enter and the spreadsheet changes.
I have a textarea that will contains a code entered by user and I want to get that code and scan it with jQuery to get the value inside a custom tag called setting then add this value to an input so the user will be able to change the value inside setting tag without touching the code. I was able to get the values and add them inside the inputs but I couldn't update the code with the new values.
HTML CODE :
<div id='tab-1'>
<textarea id='template-code' cols='67' rows='27'></textarea>
<button id='submit-code'>Submit Code</button>
</div>
<div id='tab-2' class='unactive'>
<form id='settings-form' method='POST'>
<div id='result'></div>
<button id='update-code'>Update Code</button>
</form>
</div>
CSS CODE :
.unactive {
display: none
}
jQuery CODE :
$('#template-code').change(function (){
var $that = $(this),
template_code = $that.val(),
code = '',
new_data = '',
text = '',
newCode = '';
// Extract settings from the theme and add them to #result
$(document).on('click', '#submit-code', function (){
$('#tab-1').addClass('unactive');
$('#tab-2').removeClass('unactive');
$(template_code).find('setting').each(function (i){
var $this = $(this),
setting_std = $this.text(),
setting_id = $this.attr('id');
code += '<input id="'+setting_id+'" name="'+setting_id+'" type="text" value="'+setting_std+'"><br>';
});
if(code !== ''){
$('#result').html(code);
}
});
// Update old data with the new one
$(document).on('click', '#update-code', function (){
new_data = $( "#settings-form" ).serializeArray();
$.each( new_data, function( i, new_field ) {
var start_key = "id='"+new_field.name+"'>",
end_key = '</setting>',
start = template_code.indexOf(start_key),
end = template_code.indexOf(end_key);
text = template_code.substring(start + start_key.length, end);
// THE PROBLEM IS HERE
// I want the variable template_code to contains the new value not the old one so I used replace but it seems that it doesn't work
template_code.replace(text, new_field.value);
});
$('#template-code').val(template_code);
$('#tab-1').removeClass('unactive');
return false;
});
});
This is an example of the theme code that will be added inside the textarea :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<b:include data='blog' name='all-head-content'/>
<link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,300italic,700' rel='stylesheet' type='text/css'/>
<link href='http://fonts.googleapis.com/css?family=Lora:400,400italic,700,700italic' rel='stylesheet' type='text/css'/>
<link href='https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css' rel='stylesheet'/>
<title><data:blog.pageTitle/></title>
<div id='option-panel' style='display:none!important'>
<setting id='post_thumbnail'>http://lorempixel.com/640/300/</setting>
<setting id='search_icon'>on</setting>
</div>
</head>
<body>
</body>
</html>
To understand my issue please enter to this JsFiddle and copy the code above then put it inside the textarea and click submit code, you will get two inputs the content of those inputs come from these two tags :
<setting id='post_thumbnail'>http://lorempixel.com/640/300/</setting>
<setting id='search_icon'>on</setting>
I want when the user change the value of inputs and click "update code" to change the value of setting tag inside the entire code.
Try this and see if it's what you're looking for:
HTML
<div id='tab-1'>
<textarea id='template' cols='67' rows='27'></textarea>
<button id='submit'>Submit Code</button>
</div>
<div id='tab-2'>
<form id='settings-form' method='POST'>
<div id='result'></div>
<button id='update'>Update Code</button>
</form>
</div>
JavaScript:
function wrap(data) {
var string = '';
var i, l;
string += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n";
string += "<!DOCTYPE html>\r\n";
string += "<html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>\r\n";
string += " <head>\r\n";
string += " <b:include data='blog' name='all-head-content'/>\r\n";
string += " <link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,300italic,700' rel='stylesheet' type='text/css'/>\r\n";
string += " <link href='http://fonts.googleapis.com/css?family=Lora:400,400italic,700,700italic' rel='stylesheet' type='text/css'/>\r\n";
string += " <link href='https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css' rel='stylesheet'/>\r\n";
string += " <title><data:blog.pageTitle/></title>\r\n";
string += " </head>\r\n";
string += " <body>\r\n";
string += " <div id='option-panel' style='display:none!important'>\r\n";
for (i = 0, l = data.length; i < l; i++)
string += " " + data[i].toString() + "\r\n";
string += " </div>\r\n";
string += " </body>\r\n";
string += "</html>\r\n";
return string;
}
$("#submit").on('click', function() {
var virtual = document.createElement("div");
var temp = '';
virtual.innerHTML = $("#template").val();
$(virtual).find('setting').each(function(i) {
var $this = $(this),
setting_std = $this.text(),
setting_id = $this.attr('id');
temp += '<input id="' + setting_id + '" name="' + setting_id + '" type="text" value="' + setting_std + '"><br>';
});
if (temp !== '')
$('#result').html(temp);
});
$("#update").on('click', function(event) {
var temp = [];
event.preventDefault();
$("#result").find("input").each(function() {
temp.push("<setting id=\"" + this.id.toString() + "\">" + this.value.toString() + "</setting>");
});
$("#template").val(wrap(temp));
});
I believe that does what you're looking for? Even though you're using jQuery, I think you ended up making it a lot harder than it had to be. I used a virtual node to quickly/easily find and pull ONLY the setting tag from the textarea on submit (down and dirty, I suppose?).
I removed the styles and whatnot since it was interfering with rapid testing, and you'll need to apply proper sanity checking/validation against user input.
Edit: Updated answer to include a ghetto wrapping function to elucidate the concept. I would not recommend using it as is, but instead utilizing a real template, which would require work outside the scope of this question.
Most recent JSFiddle after editing: http://jsfiddle.net/zo3hh2ye/6/
Here's another version of the code. I saved the new values in an array and then replaced them with the existing values in the textarea text. Give a try and see if that solves your problem.
Script :
<script type="text/javascript">
$('#template-code').change(function () {
var $that = $(this),
template_code = $that.val(),
code = '',
new_data = '',
text = '',
newCode = '';
// Extract settings from the theme and add them to #result
$('#submit-code').click(function () {
$('#tab-1').addClass('unactive');
$('#tab-2').removeClass('unactive');
$(template_code).find('setting').each(function (i) {
var $this = $(this),
setting_std = $this.text(),
setting_id = $this.attr('id');
code += '<input id="' + setting_id + '" name="' + setting_id + '" type="text" value="' + setting_std + '"><br>';
});
if (code !== '') {
$('#result').html(code);
}
});
// Update old data with the new one
$('#update-code').click(function () {
new_data = $("#settings-form").serializeArray();
$(template_code).find('setting').each(function (i) {
template_code = template_code.replace("<setting", "").replace("id='" + $(this).attr("id") + "'>", "").replace($(this).html(), "{" + i + "}").replace("</setting>", "");
});
$.each(new_data, function (i, new_field) {
template_code = template_code.replace("{" + i + "}", "<setting id='" + new_field.name + "'>" + new_field.value + "</setting>");
});
$('#template-code').val(template_code);
$('#tab-1').removeClass('unactive');
return false;
});
});
</script>
HTML Template :
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<b:include data='blog' name='all-head-content'/>
<link href='http://fonts.googleapis.com/css?family=Open+Sans+Condensed:300,300italic,700' rel='stylesheet' type='text/css'/>
<link href='http://fonts.googleapis.com/css?family=Lora:400,400italic,700,700italic' rel='stylesheet' type='text/css'/>
<link href='https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css' rel='stylesheet'/>
<title><data:blog.pageTitle/></title>
<div id='option-panel' style='display:none!important'>
<setting id='post_thumbnail'>text1</setting>
<setting id='search_icon'>text2</setting>
</div>
</head>
<body>
</body>
</html>
I couldn't replace the text 'on' in the template you provided, not sure if it has something to do with some reserved key word but everything else works fine.
I am making a search feature, that searches through a JSON object and finds the person you are looking for and displays some information about them, but for now it only works if you search for either the first name of the person or the last name separately. I want the search to be able to find the person even if I type both the first name and the last name in the same text field.
<!DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-2.1.0.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("#getValue").click(function(){
var typed = $("#typedText").val();
$(function(){
$.getJSON('json.json', function(data) {
var search = $(data.GetContactsResult).filter(function (i, n) {
if (n.FirstName === typed || n.LastName === typed) {
return n.LastName
}
});
console.log(search);
for (var i = 0; i < search.length; i++) {
alert(search[i].FirstName + " " + search[i].LastName + " " + search[i].MobileNumber + " " + " " +search[i].StateCaption)
}
});
});
});
});
</script>
<meta charset="utf-8">
<title>click function</title>
</head>
<body>
<input type="text" id="typedText">
<input type="button" id="getValue" value="Type value">
</body>
</html>
I'm not that experienced with JavaScript or jQuery so please tell me if my current solution is a mess and can be done better.
Turn your logic around inside the filter.
var search = $(data.GetContactsResult).filter(function (i, n) {
if (typed.indexOf(n.FirstName) !== -1 || typed.indexOf(n.LastName) !== -1) {
return n.LastName;
}
});
I've put together a little app using Kendo UI that stores user inputs in a Javascript array, and then prints these items out by adding text to a div. Along with the text, I need to have a delete button to remove these items from the array.
Since I'm adding the delete buttons to the DOM after I initialize Kendo UI, I assume I need to use the .kendoMobileButton() method on each button I add. If I don't do this, my buttons aren't styled correctly, despite being given the attribute data-role="button".
Still, when I try to use these buttons, I can't get them to call a function with data-click="deleteNumber". The function simply doesn't seem to fire. Any hints?
Here is a quick example I threw together that illustrates my problem: http://crocdoc.ifas.ufl.edu/files/kendo_example/
It is pasted here for easy reference:
<!DOCTYPE HTML>
<html>
<head>
<title>Example code</title>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/kendo.mobile.all.min.css" type="text/css">
<script src="js/jquery.min.js"></script>
<script src="js/kendo.mobile.min.js"></script>
</head>
<body>
<div data-role="view" id="main">
<div data-role="header">
<div data-role="navbar">Generate numbers</div>
</div>
<a data-role="button" data-click="addNumber" style="display: block; margin: 10px; text-align: center;">Add number</a>
<div id="number_list" style="padding: 0 20px 0 20px;"></div>
</div>
<script>
var app = new kendo.mobile.Application();
var number_container = [];
var addNumber = function () {
var current_number = Math.floor(Math.random() * 1000 + 1);
number_container.push(current_number);
console.log(number_container);
var current_index = number_container.length - 1;
$('#number_list').append('Number ' + current_index + ': ' + current_number + ' <a id="delete_' + current_index + '" data-role="button" data-click="deleteNumber">Delete</a><br >');
$('#delete_'+current_index).kendoMobileButton();
};
var deleteNumber = function (e) {
console.log('Delete button hit');
var button_id = e.button.context.id;
button_id = button_id.replace('delete_', '');
button_id = parseFloat(button_id);
number_container.splice(button_id, 1);
$('#number_list').empty();
for (var i = 0; i < number_container.length; i++) {
$('#number_list').append('Number '+i+': '+number_container[i]+' <a id="delete_' + i + '" data-role="button" data-click="deleteNumber">Delete</a><br >');
}
};
</script>
</body>
</html>
You need to modify your code as following ,
$('#number_list').append('Number ' + current_index + ': ' + current_number + ' <a id="delete_' + current_index + '">Delete</a><br/>');
$('#delete_'+current_index).kendoMobileButton({click: deleteNumber});
Thanks
DJ
#debug_mode
This is driving me freaking BATTY as hell.
This is essentially what I am trying to accomplish.
You'll see the Json there are a 2 different departments "Office" and "Stockroom"
What I am trying to accomplish is to arrange everyone by the department they are in. The problem with this is that the HTML needs to look something like this
<h3>Section 1</h3>
<div>
<p>
First Paragraph
</p>
<p>
Second Paragraph
</p>
<p>
Third Paragraph
</p>
</div>
But unfortunately I cannot seem to get the </div> tag in the right spot at the end of the last paragraph of each section
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<link rel="stylesheet" type="text/css" href="http://jqueryui.com/themes/base/jquery.ui.all.css" />
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="http://jqueryui.com/ui/jquery.ui.core.js"></script>
<script type="text/javascript" src="http://jqueryui.com/ui/jquery.ui.widget.js"></script>
<script type="text/javascript" src="http://jqueryui.com/ui/jquery.ui.accordion.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var contacts = [{"displayname":"Bruce Lee","email":"Bruce.lee#karate.com","department":"stockroom"},
{"displayname":"Your Momma","email":"Your.Momma#Yourmom.com ","department":"stockroom"},
{"displayname":"Bob","email":"Bob#bob.com ","department":"Office"},
{"displayname":"Cathy","email":"Cathy#Cathy.com ","department":"Office"},
{"displayname":"mike","email":"mike#Cathy.com ","department":"Office"},
{"displayname":"scott","email":"scott#Cathy.com ","department":"Office"}
];
var contacts2 = contacts;
var r = 1;
var lastvalue = 'blah';
for(var i=0; i <=contacts.length; i++)
{
if(contacts[i].department != null)
{
if(lastvalue != contacts[i].department)
{
if(i<1)
{
$('#accordion').append('</div><h3>' + contacts[i].department + '</h3>');
$('#accordion').append('<div><p>' + contacts[i].displayname + '</p>');
}else{
$('#accordion').append('<h3>' + contacts[i].department + '</h3>');
$('#accordion').append('<div><p>' + contacts[i].displayname + '</p>');
}
}else{
$('#accordion').append('<p>' + contacts[i].displayname + '</p>');
}
lastvalue = contacts[i].department;
r++;
}
}
});
$(function() {
$( "#accordion" ).accordion();
});
</script>
</head>
<body>
<div id="contactlist">
<div id="accordion">
</div>
</div>
</body>
</html>
You might want to change this to a jquery each loop and work with json objects directly inside it. The reason you were getting an accordion level each time was due to your loop inserting a h3 every time. I've supplied code to get you pretty much what you need.
edit:
Here is a link to my forked jsfiddle => http://jsfiddle.net/TTV6d/12/
Hope this helps
var departmentlist=new Array();
$.each(contacts, function(i,contact) {
//insert the departments
if (contact.department != null && $('#' + contact.department).length == 0) {
$('#accordion').append('<h3 id='+ contact.department +'>' + contact.department + '</h3>');
departmentlist.push(contact.department);
}
//insert contacts in the accordion
$('#' + contact.department).after('<p>' + contact.displayname + '</p>');
});
$.each(departmentlist, function(i,list) {
$("#" + list).nextUntil("h3").wrapAll("<div></div>");
});
First, be aware that browsers will generally attempt to normalize markup appended to the document: (http://jsfiddle.net/EVjaq/)
$('#container').append('<div><p>Testing</p>'); // no `</div>`
console.log($('#container').html()); // `<div><p>Testing</p></div>`
So, the open <div> in this line will be closed at the end for you, before the next <p> is appended:
$('#accordion').append('<div><p>' + contacts[i].displayname + '</p>');
To avoid this, you can store all of the HTML in a variable, concatenating segments together, and append it to the DOM only once it's done. Or, you can store the <div> and append to that, which can make the conditionals a bit simpler:
var lastvalue = null,
lastdiv = null;
for (var i = 0; i <= contacts.length - 1; i++) {
if (contacts[i].department != null) {
if (lastvalue != contacts[i].department) {
$('#accordion').append('<h3>' + contacts[i].department + '</h3>');
// remember `<div>`
lastdiv = $('<div>').appendTo('#accordion');
}
// append to the `<div>` directly rather than `#accordion`
// also, skip the `else` so you don't have to type this twice
lastdiv.append('<p>' + contacts[i].displayname + '</p>');
lastvalue = contacts[i].department;
r++;
}
}