7

I had a coding interview quiz for front-end working with JSON and whatnot. I submitted my file but I'd just like to learn what I was missing.

And one of the reqs was Should not require a web server, and should be able to run offline..

I used jQuery and used $.getJSON() to get the data from the .JSON file. I threw it up on my WAMP localserver and it worked flawlessly across all three major browsers (IE, Firefox, Chrome). Then I moved that project to Desktop, so essentally, without a LOCALSERVER.

On Firefox 30.0, it worked great. No problems.

Oon Google Chrome, I know you can't access local files without a web server...

On Internet Explorer 11, however... it didn't work. Why?

Here is what I am using. It's not complex.

function loadTasks() {
  console.log("Loading tasks...");
  $.getJSON("data.json", function(result) {
    $.each(result, function(i, task) {
      $("#load_tasks").append(
        "<div class='row'><span class='data-task'>" + task.name +
        "</span> <span class='data-date'>" + task.date +
        "</span> <span class='data-name'>" + task.assigned +
        "</span> </div>");
    });
  });
}

and here is data.json

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
test
  • 17,706
  • 64
  • 171
  • 244
  • @SuperHornet in IE? No. – test Jul 23 '14 at 08:14
  • do you see any data in result in debug mode? – Super Hornet Jul 23 '14 at 08:17
  • Nothing... @SuperHornet – test Jul 24 '14 at 06:20
  • Which version of jQuery are you using? – MIdhun Krishna Jul 28 '14 at 08:59
  • 2
    Also go to Internet Options>Advanced tab, check "Enable Active Content to run in files on My Computer". Can you reply with results of this? – MIdhun Krishna Jul 28 '14 at 09:14
  • Have you tried a plain json without no return (`/r/n`)? I was wondering a possible parsing problem on `nyro.net/data.json`. – falsarella Jul 30 '14 at 00:16
  • what is the version of Jquery that u're using ? – Arkantos Jul 31 '14 at 18:52
  • 1
    nyro.net/data.json does not exist... – Kai Hartmann Aug 01 '14 at 06:18
  • I tried your code with a custom json file (since your linked file does not exist), and everything worked fine in IE 11. But IE notified me, that it blocked script/activeX execution, and I had to explicitly allow it. Might it be, that just your IE settings are too restrictive in this regard? – Kai Hartmann Aug 01 '14 at 06:41
  • If you need to access a json file that is next to your html you don't need to fetch it with jquery or ajax. You just need to include it in your page. I answered that question over here: http://stackoverflow.com/questions/24950799/getting-data-from-json-file/24954089#24954089 If you can confirm this works for you, we can mark this one as a duplicate. – Brunis Aug 02 '14 at 11:41

10 Answers10

6

This seems to be a bug in jQuery. This bug has been reported to jQuery. The bugs status is fixed. But it seems, the bug is still at large.

Explanation

Generally in IE, ajax is implemented through ActiveXObjects. But in IE11, they made some tweaks to ActiveXObject implementation that if we try to do the following:

typeof(window.ActiveXObject)

instead of returning 'function', as it is said in IE docs, it returns undefined. jQuery used to use this to switch between xhr in normal browsers and between one in IE. Since the check evaluates to undefined, code used to create xhr object in normal browsers is run.(which of-course is a bug, strangely, for non-local files it working fine).

In a bug filed to bugs.jquery.com, the bug reporter asks,

To fix the problem it's enough to change the condition: use "window.ActiveXObject !== undefined ?" instead of "window.ActiveXObject ?"

jQuery developers does try to fix this with this commit, but the comment under the commit says its still not fixed and also suggests a possible way to approach this problem.

var activex; // save activex somewhere so that it only need to check once
if ( activex === undefined ) 
  try { 
    new ActiveXObject("MSXML2.XMLHTTP.3.0");
    activex = true; 
  } catch (e) { 
    activex = false 
  }
xhr = activex ? createActiveXHR() : createStandardXHR(); 
MIdhun Krishna
  • 1,739
  • 1
  • 13
  • 31
3

I tried running your code in my machine and it works fine in IE. However if this is not running in your machine there should be some issue with IE settings. Apart from this if you want to read local file you can try the below code to resolve this issue for IE

function showData(){
function getLocalPath(fileName/*file name assuming in same directory*/){
    // Remove any location or query part of the URL
    var directoryPath = window.location.href.split("#")[0].split("?")[0];
    var localPath;
    if (directoryPath.charAt(9) == ":") {
        localPath = unescape(directoryPath.substr(8)).replace(new RegExp("/","g"),"\\");
    }
    localPath = localPath.substring(0, localPath.lastIndexOf("\\")+1)+fileName;
    console.log(localPath);
    return localPath;
}

var content = null;
    try {
        var fileSystemObj = new ActiveXObject("Scripting.FileSystemObject");
        var file = fileSystemObj.OpenTextFile(getLocalPath("data.json"),1);
        content = file.ReadAll();
        file.Close();
    } catch(ex) {
        console.log(ex);
    }
    console.log(content);
}
showData();

Run your html file in browser from file path and try running above function in console. It will output the content of json file in console.

You can create a wrapper for above code to use in XHR request. Let me know if you need help in integrating this with jQuery AJAX request.

khagesh
  • 948
  • 1
  • 6
  • 18
1

What you we're missing was the use of appCache,

<html manifest="example.appcache">

in your HTACCESS add

AddType text/cache-manifest .appcache

inside example.appcache

CACHE MANIFEST
data.json
index.php
someimage.png
# continue for all the file needed for the web site to work

This means that once you have connected and downloaded the content once it's not needed again. on another note you not supposed to be able to access a file:// URI though XHR/ajax as there is no way to send the content if you wanted it offline you could have just embedded the content of the json file into you code as a string and just use var jsonStr = '{}'; var jsonObj = JSON.parse(jsonStr); where jsonStr is you code. this would have meant no connections to the server as there would be no ajax/XHR request

Barkermn01
  • 6,781
  • 33
  • 83
1

jQuery .getJSON uses ajax. http://api.jquery.com/jquery.getjson/

.ajax uses a XMLHttpRequest

The web security of chrome and other browsers block XMLHttpRequest to local files because it is a security issue.

Via Security in Depth: Local Web Pages

http://blog.chromium.org/2008/12/security-in-depth-local-web-pages.html

You receive an email message from an attacker containing a web page as an attachment, which you download.

You open the now-local web page in your browser.

The local web page creates an iframe whose source is https://mail.google.com/mail/.

Because you are logged in to Gmail, the frame loads the messages in your inbox.

The local web page reads the contents of the frame by using JavaScript to access frames[0].document.documentElement.innerHTML. (An Internet web page would not be able to perform this step because it would come from a non-Gmail origin; the same-origin policy would cause the read to fail.)

The local web page places the contents of your inbox into a and submits the data via a form POST to the attacker's web server. Now the attacker has your inbox, which may be useful for spamming or identify theft.

The solution for data which does not need same-origin policy security, is padded json. Since jsonp is not a secure format for data. Jsonp does not have the same-origin policy.

/* secured json */
{
  "one": "Singular sensation",
  "two": "Beady little eyes",
  "three": "Little birds pitch by my doorstep"
}

/* padded json aka jsonp */
Mycallback ({
  "one": "Singular sensation",
  "two": "Beady little eyes",
  "three": "Little birds pitch by my doorstep"
});

Since with jsonp the json is wrapped in a valid javascript function it can be opened the same way as any one would add any javascript to a page.

var element = document.createElement("script");
element.src = "jsonp.js";
document.body.appendChild(element);

And your callback processes the data,

function Mycallback(jsondata) {
}

This is functionally the same as a ajax request but different because it is a jsonp request, which is actually easier.

jQuery libs do directly support jsonp as well http://api.jquery.com/jquery.getjson/ See the example using Flickr's JSONP API; unless one was aware of the dual standards they may not even notice that jsonp is being used.

(function() { /* jsonp request note callback in url, otherwise same json*/
  var flickerAPI = "http://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?"; 
  $.getJSON( flickerAPI, {
    tags: "mount rainier",
    tagmode: "any",
    format: "json"
  })
    .done(function( data ) {
      $.each( data.items, function( i, item ) {
        $( "<img>" ).attr( "src", item.media.m ).appendTo( "#images" );
        if ( i === 3 ) {
          return false;
        }
      });
    });
})();

Local access to json can be enabled but it is done differently depending on browswer.

Use --allow-file-access-from-files to enable it in chrome. https://code.google.com/p/chromium/issues/detail?id=40787

FYI: they are working on encripted json https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-encryption-08 I am fairly certain that there will be no method of using this locally the intention is to make it really, really secure.

Community
  • 1
  • 1
Wayne
  • 4,760
  • 1
  • 24
  • 24
  • This is fine example of an answer however you forgot one thing, jsonp can never be cached as the content changes as jQuery create the function name on the fly so the content is ever changing and this would prevent the use offline and i doubt that the interviews wanted to change the browser start up parameters to run his code kind of breaks the point of being a Web Application if people have to change browser start up options to use it... – Barkermn01 Aug 05 '14 at 10:46
0

Source: https://stackoverflow.com/a/22368301/1845953

Posting the answer just in case somebody else runs into it. In my case IE was loading a version of jquery that apparently causes "JSON undefined" error. Here is what I did to solve it:

<!--[if lt IE 9]>
    <script src="http://code.jquery.com/jquery-1.10.2.js"></script>
<![endif]-->
<!--[if gte IE 9]><!-->
    <script src="http://code.jquery.com/jquery-2.0.3.js"></script>
<!--<![endif]-->

The latest one is jquery 2.1.1: direct link but it says:

(IE <9 not supported)

So I guess jquery 1.11.1: direct link

And I found out you can develop ajax and jquery stuff in Chrome on local files if you use Chrome with this flag: --allow-file-access-from-files (source)

Community
  • 1
  • 1
VixinG
  • 1,387
  • 1
  • 17
  • 31
0
<meta http-equiv="X-UA-Compatible" content="IE=edge"> 

Try adding this meta tag and check in IE

Sweetz
  • 344
  • 1
  • 4
  • 17
0

Here's a working solution. I've included handlebars because it's cleaner.

<!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>JSON TEST</title>

    </head>
    <body>

        <div id="load-tasks">

        </div>


        <script src="jquery.min.js"></script>
        <script src="handlebars.min.js"></script>


        <script id="tasks-template" type="text/x-handlebars-template">
            {{#each .}}
            <div class="row">
                <span class="data-task">
                    {{this.name}}
                </span> <span class="data-date">
                    {{this.date}}
                </span> <span class="data-name">
                    {{this.assigned}}
                </span>
            </div>
            {{/each}}
        </script>

        <script>
            $(function () {
                var loadTasksContainer = $('#load-tasks'),
                    tasksTemplate = Handlebars.compile($('#tasks-template').html());

                $.ajax({
                    type: "GET",
                    url: "data.json",
                    dataType: "json",
                    cache: false,                    
                    success: function (data) {
                        var html = tasksTemplate(data);
                        loadTasksContainer.append(html);
                    },
                    error: function (xhr, status, error) {
                        //log error and status
                    }
                });

            });
        </script>
    </body>
    </html>
Matija Grcic
  • 12,963
  • 6
  • 62
  • 90
0

Using JSONP you could make this work for all browsers with or without a web server or even cross domain.

Example data.jsonp file:

loadTasks([
    {name:"Task 1", date:"Date 1", assigned:"John Doe"},
    {name:"Task 2", date:"Date 2", assigned:"Jane Doe"}
]);

Then on your page just load the data.jsonp using a script tag:

<script>
function loadTasks(tasks) {
    $.each(tasks, function (i, task) {
        $("#load_tasks").append(
            "<div class='row'><span class='data-task'>" + task.name +
            "</span> <span class='data-date'>" + task.date +
            "</span> <span class='data-name'>" + task.assigned +
            "</span> </div>");
    });  
}
</script>
<script src="data.jsonp"></script>
Phil McCullick
  • 7,175
  • 2
  • 29
  • 29
0

Try including an error callback ; jqxhr.responseText may still contain data.json .

data.json

{"data":{"abc":[123]}}

json.html

<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="jquery-1.11.1.min.js"></script>
<script type="text/javascript">
$(function() {
  $.getJSON(document.location.protocol + "data.json")
  .then(function(data, textStatus, jqxhr) {
    var response = JSON.parse(data);
    console.log(textStatus, response);
  } 
   // `error` callback
  , function(jqxhr, textStatus, errorThrown) {
      var response = JSON.parse(jqxhr.responseText);
      console.log(textStatus, errorThrown, response);
      $("body").append(response.data.abc);
  });
})
</script>
</head>
<body>
</body>
</html>
guest271314
  • 1
  • 15
  • 104
  • 177
0

Dealing with this problem will lead you to anywhere. It is a difficult task and it could be easily solved using any http server.

If your problem is that it is difficult to set up one, try this: https://www.npmjs.org/package/http-server

On your shell you go to the directory where are your files and then you just type

http-server ./ -p 12345 

where 12345 can be changed by any valid and not already used port of your choice.

htellez
  • 1,269
  • 2
  • 13
  • 25