Too much JSON crashes browser :( - javascript

I have an ajax country/city selector. When I select United States the browser crashes. Doh!
I have a dropdown list of countries. When I select a country a jQuery ajax call is run which gets a JSON response of cities belonging to that country.
I should have seen it coming when I had to increase my allowed memory during execution. Here's the JSON response from selecting the UK.
{"5947":"Aberdeen","12838":"Aberystwyth","15707":"Aldershot","18575":"Alsagers Bank","18682":"Altrincham","4863":"Andover","41802":"AOL","6471":"Armagh","18945":"Ascot","4864":"Ashby-de-la-Zouch","4865":"Ashford","5948":"Aviemore","12985":"Aylesbury","12281":"Ballymena","14446":"Banbury","12445":"Bangor","13631":"Barking","4866":"Barnet","17004":"Barnsley","16423":"Barrow-in-Furness","16254":"Basildon","12402":"Basingstoke","5826":"Bath","13289":"Beddgelert","15082":"Bedford","4868":"Belfast","4869":"Belper","13874":"Benfleet","5827":"Benson","15514":"Berkhamsted","4870":"Berwick Upon Tweed","12948":"Betws-y-Coed","18776":"Bexley","14530":"Bicester","4871":"Billericay","18436":"Birkenhead","4872":"Birmingham","14592":"Blackburn","14686":"Blackpool","12526":"Bolton","12480":"Bournemouth","13062":"Bracknell","18772":"Bradford","4873":"Braemar","4874":"Brecon","4875":"Brentwood","18820":"Brighton","14260":"Bristol","4876":"Broomfield","42004":"Burgess Hill","14654":"Burnley","4877":"Burton Upon Tren","13812":"Bury","15835":"Bury St Edmunds","16500":"Camberley","4878":"Cambridge","4879":"Canterbury","5957":"Cardiff","14443":"Carlisle","14065":"Carrickfergus","42384":"Chalgrove","5832":"Chatham","13641":"Chelmsford","4880":"Cheltenham","4881":"Chester","42879":"Chesterfield","12160":"Chichester","41768":"Chorley","14056":"Church Stretton","5949":"Cladich","4884":"Colchester","16204":"Congleton","17534":"Coniston","42888":"Corsham","4885":"Coventry","13575":"Crawley","15410":"Crewe","13913":"Croydon","4886":"Cumbernauld","13711":"Dartford","4887":"Dartmouth","5833":"Derby","17468":"Derry","4889":"Doncaster","13696":"Dorchester","15377":"Dorking","5834":"Dover","16659":"Dudley","41867":"Dumbarton","18091":"Dumfries","4890":"Dunbar","14217":"Dunblane","4891":"Dundee","14067":"Dunfermline","4892":"Durham","16058":"East Molesey","17521":"East Preston","12501":"Eastbourne","12374":"Eastrea","4893":"Edinburgh","18992":"Elgin","41763":"Ellesmere","12883":"Ely","16825":"Enfield","14510":"Epsom","5835":"Exeter","4894":"Falkirk","5836":"Falmouth","42388":"Faringdon","42034":"Farmington","14604":"Farnham","42347":"Feltham","12829":"Fleet","4895":"Forres","42315":"Frosterley","5950":"Glasgow","4896":"Glastonbury","12562":"Gloucester","15956":"Gosport","4898":"Grangemouth","12626":"Gravesend","16057":"Grays","4899":"Great Wilbraham","4900":"Greenock","12752":"Grimsby","11747":"Guildford","14506":"Guilford","11938":"Halifax","5010":"Hamilton","15553":"Harlow","41733":"Harpenden","14713":"Harrow","4902":"Hartlepool","18952":"Haslemere","13977":"Hastings","14917":"Hatfield","12529":"Haverfordwest","4903":"Haverhill","4904":"Hawarden","5951":"Hawick","11776":"Hemel Hempstead","15302":"Hereford","14999":"Hertford","41893":"Heston","16056":"Hexham","13019":"High Wycombe","13643":"Hoddesdon","5958":"Holyhead","12420":"Hornchurch","14160":"Horsham","12108":"Huddersfield","5837":"Hull End","13296":"Huntingdon","14801":"Hyde","17707":"Ilford","41721":"Inverkeithing","4905":"Inverness","5838":"Ipswich","4906":"Keighley","4907":"Kelso","18628":"Kendal","17805":"Kenilworth","4908":"Kennet","4909":"Kettering","18578":"Kidsgrove","18984":"Kilmarnock","4910":"Kingston Upon Hull","5952":"Kirkwall","18257":"Lakenheath","15425":"Lampeter","13182":"Lancaster","4911":"Laughton","13488":"Leamington","18824":"Leeds","13135":"Leek","17849":"Leicester","17716":"Leigh","12836":"Lerwick","13387":"Letchworth","4912":"Lewes","41767":"Leyland","13546":"Lichfield","5840":"Lincoln","19039":"Little Chalfont","16778":"Liverpool","13442":"Llandrindod Well","5953":"Loch Ness","12008":"London","15035":"Loughborough","15518":"Loughgall","15011":"Louth","18492":"Lowestoft","14023":"Luton","4913":"Machynlleth","12416":"Maidenhead","12230":"Maidstone","14722":"Manchester","4914":"Mansfield","4915":"Margate","4916":"Marlborough","17889":"Marlow","18870":"Melborne","16170":"Melton Mowbray","4917":"Merton","5844":"Middlesbrough","5959":"Milford","15181":"Millom","12315":"Milton Keynes","12089":"Mold","18816":"Montrose","5954":"Motherwell","18574":"Nantwich","4918":"Newark","17097":"Newbury","5845":"Newcastle","4919":"Newcastle Upon Tyne","19040":"Newport","41682":"Newquay","13629":"Northallerton","4922":"Northampton","18577":"Northwich","42209":"northwold","15080":"Norwich","5847":"Nottingham","4923":"Oban","11975":"Oldham","6474":"Omagh","17161":"Oxford","15422":"Oxshott","18627":"Penrith","4925":"Penzance","16404":"Perth","5848":"Peterborough","4926":"Plains","4927":"Plymouth","15551":"Pontypridd","14208":"Poole","4928":"Portsmouth","17642":"Portstewart","41766":"Preston","5011":"Prestwick","18579":"Radway Green","42069":"Ramsgate","11775":"Reading","14706":"Redditch","16276":"Ringwood","15522":"Ripon","14673":"Rochester","15968":"Romford","41857":"Rugby","15289":"Runcorn","17520":"Rustington","14052":"Saint Albans","16462":"Salford","4931":"Salisbury","42295":"Sandwich","17690":"Sandy","4932":"Scarborough","13975":"Seaford","12003":"Shaftesbury","18891":"Sheffield","5850":"Shrewsbury","13178":"Slough","14708":"Solihull","4935":"Southampton","4936":"Southborough","14524":"Southend-on-Sea","13970":"Southport","42260":"St Albans","5955":"St Andrews","15841":"St Asaph","18576":"St Helens","16114":"St Ives","12717":"Stafford","41746":"Staines","14051":"Stanmore","16656":"Stansted","42032":"Stevenage","5012":"Stirling","11801":"Stockport","14198":"Stockton-on-Tees","4937":"Stoke On Trent","42386":"Stranraer","4938":"Stratford-Upon-Avon","4939":"Stroud","18615":"Sudbury","11860":"Sunderland","16393":"Sutton","5960":"Swansea","12853":"Swindon","4941":"Taunton","5851":"Teeside","13973":"Telford","4943":"Truro","17702":"Virginia Water","5852":"Waddington","12059":"Wakefield","4945":"Wallingford","4947":"Wareham","5853":"Warrington","4948":"Warwick","4949":"Watford","12009":"Wellingborough","12528":"Wellington","13366":"Wells","12530":"Welwyn Garden City","16785":"Weston Under Lizard","16334":"Wetherby","18171":"Weymouth","4950":"Whitby","13308":"Whitehaven","42387":"Whitehead","5956":"Wick","17581":"Wilmslow","5854":"Wimbledon","12524":"Wimborne Minster","12551":"Winchester","15946":"Windsor","18573":"Winsford","4952":"Wisbech","4953":"Wisborough Green","12982":"Woking","18769":"Wokingham","13287":"Wolverhampton","17904":"Woodford","18086":"Woolavington","11783":"Worcester","12128":"Worthing","5961":"Wrexham","13630":"Yarm","17015":"Yeovil","11824":"York"}
Here is my Javascipt:
$('#current-country').change(function(){ //any select change on the dropdown with id country trigger this code
$('.select-current-city').show();
$("#current-cities > option").remove(); //first of all clear select items
var country_id = $('#current-country').val(); // here we are taking country id of the selected one.
$.ajax({
type: "POST",
url: "<?php echo base_url()?>map/get_cities/"+country_id, //here we are calling our user controller and get_cities method with the country_id
success: function(cities) //we're calling the response json array 'cities'
{
$.each(cities,function(id,city) //here we're doing a foeach loop round each city with id as the key and city as the value
{
var opt = $('<option />'); // here we're creating a new select option with for each city
opt.val(id);
opt.text(city);
$('#current-cities').append(opt); //here we will append these new select options to a dropdown with the id 'cities'
});
}
});
});
Has anyone any suggestions on how I can process this much data in the browser?
I'm using PHP (Codeigniter), MySQL and jQuery.

I would recommend creating an array of your new option nodes, and then appending them en masse. Doing them one at a time may be what's killing you.
var newOptions = [];
$.each(cities,function(id,city) {
var opt = $('<option />', { "val": id, "text": city });
newOptions.push(opt[0]); //need to push actual dom node - thanks RightSaidFred
});
$('#current-cities').append(newOptions);
Or should this be clearing previous options in the dropdown? If so:
$('#current-cities').html(newOptions);

First double check you are not making the same AJAX request more than once.
A quick and easy solution would be to split your AJAX requests in more than one. Start by dividing it into two, and if that still isn't enough, divide them by 3 or more.
You can then check which JSON size is the right one for you, and use that one. Or you can have your php determine how much requests are needed based on the number of total cities.
For really large lists, I had to split the requests into 100 items each. The first request I would get the first piece of list along with a bit of data indicating how much requests I had to make to obtain the full list, append the new nodes, then I would make the remaining requests, until I got the full list.

I don't see how that would crash for you. Here I am doing the worst possible thing and it is blazing fast:
http://jsfiddle.net/HZnYQ/
Each time you select something, I remove all elements one by one and then append them one by one, and it's still instant. Actually my CPU doesn't even make a note of it.

Related

How to get the index of one specific element within an javascript object

I have been working on this problem for the second day now and I simply can't seem to find the right way to do it.
Problem:
I need to send the index value of a chosen element within a javascript object to an action in PHP. So when the user chooses a certain element, the index value of said element within the object should be send to the mentioned PHP action.
What I have so far is the following:
The button for the jump:
<!-- Button to jumpo tp specific dashboard. The select and and option tags will be generated dynamicly -->
<div style="float:right; margin-right: 10px;">
<select name="idDashboards" id="idDashboards">
<option value="">Jump to Dashboard</option>
</select>
</div>
The Array itself as well as how the HTML elements are created
// The array with the dashboards as I get it from the server
var dashboardArray = <?php echo json_encode(CHtml::listData($dashboards, 'iddashboard', 'title')); ?>;
// Loop through the elements in the dashboardArray and then create option elements to be added to the above create select element.
for (var idDashboard in dashboardArray) {
$('#idDashboards').append($(document.createElement('option')).prop({
value: idDashboard,
text: dashboardArray[idDashboard].charAt(0).toUpperCase() + dashboardArray[idDashboard].slice(1)
}));
};
How the object looks like when I log it.
{2: "Dashboard_Test_1", 3: "Dashboard_Test_2", 4: "Dashboard_Test_3", 6: "Dashboard_Test_4"}
What I have been trying so far:
var dashboardIndex = -1;
var i;
// Loop to get the indexes.
for (i in dashboardArray) {
if (dashboardArray.hasOwnProperty(i)) {
dashboardIndex++
console.log(dashboardIndex+" These are the indexes");
}
}
// Ajax request that sends data to the PHP action
$("#idDashboards").change(function() {
var idDashboard = $(this).val();
console.log("The change happened!");
$.ajax({
url: 'Path/To/PHP/File/And/Action=' + dashboardIndex,
method: 'GET',
success: function(result) {
location.reload();
console.log("So far, so good!");
}
});
});
});
Now, the dashboardIndex and the index of the elements in the object align and are good. But how do I send the index value of only the element that has been chosen by the user?
I am very sorry if this is a duplicate but I could not find anything helpful so far. Every advice/help is appreciated.
Inside your change function, you should be able to use $(this).prop('selectedIndex') to get the index of the item you selected.
I am curious, however, why the API is looking for the index of the dashboard rather than the seemingly unique ID that your API is initially responding with. This may be a more resilient approach.

Django: populate the field based on previous field value - missing the last step to make it work

Like many, I want to populate a field in a django form based on what is selected in another field. I've read alot of answers with javascript(I struggle in javscript, so that's where I'm having trouble with the exemples), and I almost got it working, but the last step(updating the field itself) isn't working so I'd love some help with that part.
Here are the 2 fields. The first fieldthat gets populated from a query and is located in a div named #merch in the form
merchandise = forms.ModelChoiceField(label='Merchandise', queryset=Merchandise.objects.all(),
merch_price = forms.DecimalField(label='Price', min_value=0, max_value=800,
initial='0.00',decimal_places = 2, max_digits=10)
Upon selection, the second field(div named #price) should then display the price based on the merchandise selected. I created the view for the ajax request:
def check_item_price(request):
if request.method == "GET":
item = request.GET.get('item', '0')#the zero as default doesn't seem to work. To verify
price = Merchandise.objects.get(id = item)
return JsonResponse(price.item_price, safe=False)#is it safe to turn safe off?
and the url
url(r'^_item_price', views.check_item_price, name='_item_price' )
Calling the url manually works great, it returns the price in json format
And here is the javascript that is in the html form. The first part works, upon change it calls the url and a json object is returned, but the second part that should update the second field isn't working. I admit my lack of knowledge in javascript is probably at fault here. I tried many variations based on examples, none worked for me.
<script type="text/javascript">
jQuery(document).ready(function() {
$('#merch').change(function() {
var item = $(this).find(':selected').val();
$.getJSON('/classes/_item_price/',{item:item},
function(data) {
$('#price').append("<option value=" + data.value + "></option>");
});
});
});
</script>
Any pointers on what to fix in the javascript?
Thanks!
After letting it marinate in my head for 2 months, I went back to it and finally made it work. Here is the right code
jQuery(document).ready(function() {
$('#merch').change(function() {
var item = $(this).find(':selected').val();
$.getJSON('/classes/_item_price/',{item:item},
function(data) {
document.getElementById('id_merch_price').value=data;
});
});
});
</script>
First, the ID wasn't precise enough, but also the way of updating it wasn't the right one it seems. I truly feel lost anytime I have to do research on javascript or jquery. So may ways to do the same thing, it's almost impossible to learn for a casual coder like me.

jQuery Autofill textbox with information from another autofill

I am having an issue with jQuery autocomplete. Basically I have a search bar, and when you type in what you're looking for the jQuery code I have calls a php script which does a MySQL query and returns everything I need and fills in the text boxes accordingly. What I then want to do is take the value I receive from that autocomplete, and use it in another autocomplete to fill in more data. The tricky part is that the data I need to get with the 2nd query is located in a different table than the first query, which share a relationship. My question is do I need a completely separate function to do this, or can I simply put both queries in the 1 php script and have the information from the first query be used for my 2nd query.
Any help is appreciated thanks!
Here is the jQuery function:
$(function() {
/* $('#abbrev').val("");
*/
$("#q16_location16").autocomplete({
source: "location_query.php",
minLength: 1,
select: function(event, ui) {
$('#q16_location161').val(ui.item.LocationID);
$('#SystemName').val(ui.item.SystemName);
$('#SiteAddress1').val(ui.item.SiteAddress1);
$('#SiteAddress2').val(ui.item.SiteAddress2);
$('#SiteCPP').val(ui.item.SiteCPP);
$('#Contact').val(ui.item.Contact);
$('#SiteLocationHours').val(ui.item.SiteLocationHours);
}
});
});
and the php script:
/* If connection to database, run sql statement. */
if ($conn)
{
$fetch = mysql_query("
SELECT Location.LocationID,
Location.SystemName,
Location.SiteAddress1,
Location.SiteAddress2,
CONCAT_WS(' ', Location.SiteCity, Location.SiteProvince, Location.SitePostalCode) AS SiteCPP,
CONCAT_WS(' ', Location.ContactName, Location.ContactPhone, Location.ContactEmail) AS Contact,
Location.SiteLocationHours,
CONCAT_WS(' ', SystemName, SiteNameLocation, SiteAddress1, SiteCity, SiteProvince, SitePostalCode) as expr2
FROM Location
WHERE Location.SystemName like '%".mysql_real_escape_string($_GET['term'])."%'
OR Location.SiteNameLocation like '%".mysql_real_escape_string($_GET['term'])."%'
OR Location.SiteAddress1 like '%".mysql_real_escape_string($_GET['term'])."%'
OR Location.SiteCity like '%".mysql_real_escape_string($_GET['term'])."%'
OR Location.SiteProvince like '%".mysql_real_escape_string($_GET['term'])."%'
OR Location.SitePostalCode like '%".mysql_real_escape_string($_GET['term'])."% '
LIMIT 0,15");
/* Retrieve and store in array the results of the query.*/
while ($row = mysql_fetch_array($fetch, MYSQL_ASSOC)) {
$row_array['LocationID'] = $row['LocationID'];
$row_array['value'] = $row['expr2'];
$row_array['SystemName'] = $row['SystemName'];
$row_array['SiteAddress1'] = $row['SiteAddress1'];
$row_array['SiteAddress2'] = $row['SiteAddress2'];
$row_array['SiteCPP'] = $row['SiteCPP'];
$row_array['Contact'] = $row['Contact'];
$row_array['SiteLocationHours'] = $row['SiteLocationHours'];
array_push($return_arr,$row_array);
}
}
/* Free connection resources. */
mysql_close($conn);
/* Toss back results as json encoded array. */
echo json_encode($return_arr, $return_arr2);
So when the user types in "New York" they can can select that option. In my example New York has an ID of 5. I also have a query that selects different streets in new york but this is in a separate table. in my streets table however, there is a "LocationID" column that for every street in new york will have a value of 5. So I want to take that ID of 5 when a user enters in new york and generate all the streets from a different table which also have that ID. I have tried multiple things in terms of creating a new function but I am just unsure of how I would pass that ID to the function.
Thanks
You can use one PHP script for this. Here's about what I'd think the basic structure will look like:
Pass two values to "location_query.php". The first value would be the name of the table that you want to query. The second value could be the selection result from the auto-complete text box.
Create a prepared statement in "location_query.php" from the two values that were passed to "location_query.php".
Perform your query.
JSON encode the result (just like you did before).
I'd also like to point out a security concern with your code. You should be using Mysqli and prepared statements instead of PHP's MySQL and mysql_real_escape_string. mysql_real_escape_string has been shown to have security deficiencies that can lead to security breaches and PHP's MySQL class has been deprecated. Mysqli and Prepared statements are much safer, and, in my opinion, provide for cleaner code since it allows for the separation of the SQL and the parameters.
Hope this helps!
EDIT: I think I understand what you're trying to do now, but I think there's a better way to go about doing it. Instead of assigning the id value to a hidden field and trying to have jquery detect every time that field is changed, I would just do the following:
For your first text box's select method:
select:function(event, ui) {
$.get("location_query.php", {
searchterm:$(ui).val()
}, yourFunction);
}
Here's an example implementation of "queryFinished":
function queryFinished(data, textStatus, jqXHR) {
var mJSON = $.parseJSON(data);
/* mJSON is the parsed JSON data returned from your "location_query.php"
script.*/
//TODO the rest of your code
}
Here's what's going on:
We define a custom function to be called when the first text box has a new item selected. This functions only purpose is to call a GET on "location_query.php".
Then, we pass the value of the selected field from the first text box via our GET call.
We then create a function to be called when GET returns.
Finally, we parse the encoded JSON that is returned by "location_query.php". After that, you can perform whatever tasks you need with the parsed JSON (mJSON in our example).
Taking this approach keeps us from having to worry about "listening" for a value change in our hidden ID field and makes everything nice and clean.

Making a step-by-step ajax request

I'm thinking about how to change a content of a div dynamically. So, here is the ajax request:
$.ajax({
url:'/foos',
cache: false,
type: 'get',
}).done( function( foo_array ) {
for( foo in foo_array ) {
$('#foo-container').append('<div class="foo-var">'+foo+'</div>');
}
});
So bassically, this ajax append all foo-var divs from the server, but if the foo_array is too long or a big very big array there is a problem because i think that takes more and more time depending on the foo_array's length
How can I append one by one??, how can I query one by one and append in foo-container instead query all foos and make an iteration??
I want to do something like this
if(foos.hasNext()){ $.ajax..... append(foo)....}
foos is an array made by many documents from a mongodb database, so I cant get the length of the array because depends of the query's find() arguments..
I'm using nodejs, mongodb, expressjs and jquery for ajax
Sorry for my bad English, and thank you all!
EDIT 2
this is an example of the data in mongodb
{category:1, name:'robert',personal:true,option:'class'}
{category:1, name:'alfredo',personal:false,option:'class'}
{category:4, name:'ricardo',personal:true,option:'class'}
{category:1, name:'genaro',personal:true,option:'class'}
{category:2, name:'andres',personal:false,option:'class'}
{category:1, name:'jose',personal:true,option:'class'}
db.collection.find({personal:true}) // gives me 4 documents
db.collection.find({option:'class'}) // gives me 6 documents
db.collection.find({category:4}) // gives me 1 document
i dont know how many documents can get from the cursor, i need to charge one by one cause there are 5097841 documents in the databse so, ajax can take long time to return all the information, i need to query one by one if hasNext() in the cursor of mongodb
You can use skip and limit and can make multiple requests. It's like paging.The following syntax may help you
db.collection.find().skip(200).limit(100);

Using jQuery to pull text from a specific <td>

I'm running an AJAX query on an external page, and am attempting to only return the data from the County . My current script is pulling the text from all of the table cells, but I cannot for the life of me get it to simply pull the county name.
The current script that is being run:
$( ".zipCode" ).each(function( intIndex ){
var zipCodeID = $(this).attr('id');
console.log('http://www.uscounties.org/cffiles_web/counties/zip_res.cfm?zip='+zipCodeID);
$.ajax({
url: 'http://www.uscounties.org/cffiles_web/counties/zip_res.cfm?zip='+zipCodeID,
type: 'GET',
success: function(res) {
var headline = $(res.responseText).find("p").text();
console.log(headline);
$('#'+zipCodeID).empty();
$('#'+zipCodeID).append(headline);
}
});
});
An example of the page that is being queried:
http://www.uscounties.org/cffiles_web/counties/zip_res.cfm?zip=56159
This should work for all entered ZIPS. The page layout is the same, I just can't get the function to return only the county. Any help or advice would be awesome. Thanks!
With the complete lack of ids and classes on that page, you don't really have much to go on. If you have access to the source of that page, stick an id or class on the cell and make your life so much easier. If not, you'll have to use what you know about the structure of the pages to find the county. Something like this will work specifically on that one page you linked to. If other pages have slight variations this will fail:
var headline = $(res.responseText).find("table > tr:eq(2) > td:eq(3)").text();
This assumes that there is only ever one table on the page and that the county is always in the 3rd cell of the 2nd row.
You're basically screen scraping. I somehow think you'll have issues with this due to cross domain and other things, but that is ancillary to the question.
You need to walk through the resultant page. Assuming there is only ever one page on the screen, it'll look like something this:
var retVal = [];
// Basically, for each row in the table...
$('tr').each(function(){
var pTR = $(this);
// Skip the header row.
if (pTR.find('th').length == 0)
{
// This is the array of TDs in the given row.
var pCells = $('td', pTR);
retVal.push({state:$(pCells[0]).text(), place:$(pCells[1]).text(), county:$(pCells[2]).text()});
}
});
// retVal now contains an array of objects, including county.
if (retVal.length > 0)
{
alert(retVal[0].county);
}
else
{
alert('Cannot parse output page');
}
The parsing code is written to be extensible, hence you get back all of the data. With postal codes, although you will likely only ever get back one county, you'll definitely get back more places. Also note... not every zip code has a county attached for a variety of reasons, but you should get back an empty string in that case.

Categories

Resources