0

On our site, the <body> tag holds various data-*="*" attributes:

<body data-someData="someValue" data-someOtherData="someOtherValue">

I need to get the values of these attributes while on other pages. So, I am using jQuery AJAX $.get to get the page's HTML, and thus these data attributes.

My current script:

// The call (used on different pages)
var stock = getProductData('stock', '/some/product/url');

// The "GET" function
function getProductData(type, url) {
    var jqxhr = $.get(url, function( data ) {
        console.log('Data found!');
        var $body = $(data).find('body');
        var val = $body.data('stock');
        console.log('Returning Value: "' + val + '"');
        return val;
    }).done(function(){
        // Request is complete
        console.log('getProductData Finished...');
    }).fail(function(){
        console.error( 'getProductData: ' + type + ' = FAIL / URL: ' + url);
    });
}

So, what's the problem? Well, the $(data).find('body').data('stock'); is coming back as undefined. I also tried $(data).find('body').attr('data-stock');, but it returned the same thing.

So, how can I return the body tag's data-someData="someValue" attribute values using $.get?

The data-stock attribute used in the example above looks like this on my product page:

<body data-stock="3">

EDIT: Not a duplicate: this question refers specifically to the parsing of specific attributes of elements. I am not asking how to just return the data using AJAX.

Derek Foulk
  • 1,892
  • 1
  • 19
  • 37
  • 1
    Why would you put these in the `body`? – Digital Chris Apr 17 '15 at 19:03
  • 1
    I agree with @DigitalChris, Making a request which downloads a heap of HTML just to get two properties is very wasteful. Why not make a separate endpoint which returns the properties as JSON? – Rory McCrossan Apr 17 '15 at 19:05
  • possible duplicate of [How to return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – user229044 Apr 17 '15 at 19:05
  • So, you get `Returning Value: undefined`? If that's the case, then you are likely not finding the correct element. jQuery strips away some elements with building a collection, `body` may be one of them. Note that you cannot return `val` from `getProductData` like this anyway. – Felix Kling Apr 17 '15 at 19:05
  • You can't create a body element with jQuery like this, i.e. `$("")` returns an empty collection. You may need to approach this differently. – Explosion Pills Apr 17 '15 at 19:05
  • I think you will either need to do string manipulation, or perhaps, if it's valid XHTML, load it as an XML object and parse it that way. jQuery, as mentioned, can't create a body because it can't add a second body tag to the DOM. You can only have one. – dmeglio Apr 17 '15 at 19:11
  • @Digital Chris - I have my reasons. Mainly NitroSell's templates restricting my usage of server-side functions using their self-made "NitroScript" language. But this is irrelevant. My question still stands, is there a way to pull the data from an element via AJAX? – Derek Foulk Apr 17 '15 at 19:45
  • @Rory McCrossan - It is all that I have to work with. And trust me, I am using them for much more than two requests. This is not a code review, I am asking a specific question that does not have to do with the efficiency of my code. – Derek Foulk Apr 17 '15 at 19:47
  • @Felix Kling - I suppose the reason I feel this is possible is because I can return the `.text()` of an element using similar requests (as long as the `data` is used as an object). – Derek Foulk Apr 17 '15 at 19:48
  • @Explosion Pills - No idea what you are talking about, I am not creating any body tags. I am trying to get the values of the body tag's attributes. – Derek Foulk Apr 17 '15 at 19:49
  • Did you try `$($.parseHTML(data)).find('body');`? *edit:* Uh, that doesn't seem to work either, `html` and `body` are still stripped. – Felix Kling Apr 17 '15 at 19:50
  • @dman2306 - Thank you for the idea. It may ultimately be what I have to do. The doctype is html5, and validates fine. It's funny that you mention XML, so you are not able to parse the returned HTML the same as you can XML? – Derek Foulk Apr 17 '15 at 19:53
  • The issue is that jQuery strips certain elements because they cannot be inserted into an existing document anyways. That includes `html` and `body` for example. In general, of course you can parse any HTML on the client, but jQuery is special... – Felix Kling Apr 17 '15 at 19:54
  • @Felix Kling - I haven't. I think that the body tag is ignored like everyone else is saying. That would explain why a lot of the things I have tried don't work. I'm about to say "screw it" and use JSON instead. – Derek Foulk Apr 17 '15 at 19:54
  • Duplicate? No. I know how to return the data. I am asking about parsing through the returned data in a specific manner. – Derek Foulk Apr 17 '15 at 19:57
  • @derekmx271 the problem is XML and HTML aren't 100% equivalent. HTML is less strict. For example, you can do `
    ` in HTML but in XML you need a closing tag. If you want something without a closing tag, you need to do `
    `. HTML doesn't care. However, if you have `
    ` in your file, an XML parser will get confused.
    – dmeglio Apr 17 '15 at 20:11
  • The part that confuses me is why "***data***" (in example I provided) is able to be used as an object, in fact in all other ajax requests I have used on this site have been "***$(data)***" as opposed to "***data***". Since it's an object, and I can use (for example) `return $(data).find('#someId').text()` and it returns the text of that element. But `.data()` specifically will not work (at least not on the body tag...). It may work on an element *inside* the ``. IDK! – Derek Foulk Apr 17 '15 at 23:51
  • It just seems like it would be much cleaner to be able to use the **.data()** instead of resorting to `` and using `.text()` - which (btw) "gets" just fine. – Derek Foulk Apr 17 '15 at 23:52

2 Answers2

0

Let me suggest perhaps a crazy idea... You provided a sample url of: var stock = getProductData('stock', '/some/product/url'); So is the file on the same domain? If so, what about loading it into a hidden iframe and then accessing the iframe's document property? That won't work though if the content is on another domain for security reasons. If it's the same domain, then window.frames["framename"].document would give you the contents of the IFrame if I'm not mistaken.

dmeglio
  • 2,830
  • 1
  • 19
  • 24
0

Okay, so the short answer is that you are not able to reference data attributes inside the <body> tag in this way. Thus, you are not able to use var productData = $(body).data('someData');. jQuery evidently does not pay attention to the body tag when using $.ajax- even if the returned data is referenced as an object.

What you can do is:

  • Move the data attributes to a hidden input (or any other tag inside the )

After doing this, you can then reference data attributes in your ajax requests.

There was one other thing I was doing wrong in my OP... My ajax request function kept returning 'undefined'. This was because the variable was being used externally before the ajax request was complete. So the return val; did not return anything until after the scripts that used the stock variable were done using it.

So, I had to use a callback and rework my script a little bit, but now everything works fine...

So- bottom line: you cannot return .data() from the body tag, but you can from other tags inside of the .

For anyone needs some help with this, here is my script (that works)...

The Call

// Needed for use inside AJAX request
var $el = $(this);
var url = $el.data('url');

// List Stock
getProductData('stock', url, function(val) {

  var stock = val;
  var str;
  var cls;

  if ( stock > 0 ) {
    str = stock + ' ' + 'In Stock';
    cls = 'in-stock';
  } else {
    str = 'Out of Stock';
    cls = 'out-of-stock';
  }

  $el.find('label.stock').addClass(cls).text(str);

});

The AJAX Request

// Get data from the product page
function getProductData(type, url, callBack) {

  $.ajax({
    url: url,
    method: 'GET',
    dataType: 'html',
    success: function(data){

      var $data = $(data).find('#product-data');

      var val = $data.data(type);

      return callBack( val );

    },
    error: function(data) {
      console.error( 'getProductData: ' + type + ' = FAIL / URL: ' + url );
    }
  });

}
Derek Foulk
  • 1,892
  • 1
  • 19
  • 37