35 votes

Quelle est la meilleure pratique pour analyser le contenu distant avec jQuery ?

Après un appel jQuery ajax pour récupérer un document XHTML entier, quelle est la meilleure façon de sélectionner des éléments spécifiques dans la chaîne de caractères résultante ? Peut-être existe-t-il une bibliothèque ou un plugin qui résout ce problème ?

jQuery ne peut sélectionner les éléments XHTML qui existent dans une chaîne que s'ils sont normalement autorisés dans un div dans la spécification du W3C ; par conséquent, je suis curieux de sélectionner des éléments tels que <title> , <script> et <style> .

Selon la documentation de jQuery :

http://docs.jquery.com/Core/jQuery#htmlownerDocument

La chaîne HTML ne peut pas contenir qui ne sont pas valides à l'intérieur d'un div, tels que les éléments html, head, body ou title.

Par conséquent, puisque nous avons établi que jQuery ne fournit pas un moyen de le faire, comment pourrais-je sélectionner ces éléments ? Par exemple, si vous pouvez me montrer comment sélectionner le titre de la page distante, ce serait parfait !

Merci, Pete.

31voto

David Burrows Points 3023

Au lieu de pirater jQuery pour faire cela, je vous suggère de laisser tomber jQuery pour une minute et d'utiliser les méthodes brutes de XML dom. En utilisant les méthodes XML Dom, vous pouvez faire ceci :

  window.onload = function(){ 
    $.ajax({
          type: 'GET', 
          url: 'text.html',
          dataType: 'html',
          success: function(data) {

            //cross platform xml object creation from w3schools
            try //Internet Explorer
              {
              xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
              xmlDoc.async="false";
              xmlDoc.loadXML(data);
              }
            catch(e)
              {
              try // Firefox, Mozilla, Opera, etc.
                {
                parser=new DOMParser();
                xmlDoc=parser.parseFromString(data,"text/xml");
                }
              catch(e)
                {
                alert(e.message);
                return;
                }
              }

            alert(xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue);
          }
    });
  }

Pas besoin d'utiliser des iframes, etc.

5voto

gnarf Points 49213

Juste une idée - testée dans FF/Safari - semble fonctionner si vous créez une iframe pour stocker le document temporairement. Bien sûr, si vous faites cela, il pourrait être plus intelligent d'utiliser la propriété src de la iframe pour charger le document et faire ce que vous voulez dans le "onload" de celui-ci.

  $(function() {
    $.ajax({
      type: 'GET', 
      url: 'result.html',
      dataType: 'html',
      success: function(data) {
        var $frame = $("<iframe src='about:blank'/>").hide();
        $frame.appendTo('body');
        var doc = $frame.get(0).contentWindow.document;
        doc.write(data);
        var $title = $("title", doc);
        alert('Title: '+$title.text() );
        $frame.remove();
      }
    });

J'ai dû ajouter la iframe au corps pour qu'elle ait un .contentWindow.

3voto

abernier Points 4115

Inspiré de cette réponse mais avec du sucre différé et du café :

fetchDoc = (url) ->
  dfd = $.Deferred()

  $.get(url)
    .done((data) ->
      # Create an iframe to push data in
      $iframe = $('<iframe style="display:none;"/>').appendTo('body')

      $doc = $iframe.contents()
      doc  = $doc[0]

      # Once iframe loaded (with markup)
      $iframe.load(->
        dfd.resolveWith(doc)
        $iframe.remove()
      )

      # Push data in
      doc.open()
      doc.write(data)
      doc.close()
    )
    .fail(dfd.reject)

  dfd.promise()

Et le fumer avec :

fetchDoc('/foo.html').done(->
  alert $('title', this).text()
)

DÉMO EN DIRECT (cliquez sur "Exécuter")

2voto

Ben Koehler Points 4707

Que diriez-vous de renommer rapidement les tags ?

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

        data = data.replace(/html/g, "xhtmlx");
        data = data.replace(/head/g, "xheadx");
        data = data.replace(/title/g, "xtitlex");
        data = data.replace(/body/g, "xbodyx");

        alert($(data).find("xtitlex").text());
    }

});

2voto

jitter Points 35805

Ça marche. J'ai juste séparé les blocs de construction pour une meilleure lisibilité.

Consultez l'explication et les commentaires en ligne pour comprendre le fonctionnement de ce système et pourquoi il doit être conçu de cette manière.

Bien sûr, cela ne peut pas être utilisé pour récupérer du contenu inter-domaine, pour cela vous devez soit faire passer les appels par un script ou penser à intégrer quelque chose comme flXHR (Ajax inter-domaines avec Flash)

appel.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>asd</title>
    <script src="jquery.js" type="text/javascript"></script>
    <script src="xmlDoc.js" type="text/javascript"></script>
    <script src="output.js" type="text/javascript"></script>
    <script src="ready.js" type="text/javascript"></script>
  </head>
  <body>
    <div>
      <input type="button" id="getit" value="GetIt" />
    </div>
  </body>
</html>

jquery.js est (jQuery 1.3.2 non compressé) test.html un document XHTML valide

xmlDoc.js

// helper function to create XMLDocument out of a string
jQuery.createXMLDocument = function( s ) {
  var xmlDoc;
  // is it a IE?
  if ( window.ActiveXObject ) {
    xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
    xmlDoc.async = "false";
    // prevent erros as IE tries to resolve the URL in the DOCTYPE
    xmlDoc.resolveExternals = false;
    xmlDoc.validateOnParse = false;
    xmlDoc.loadXML(s);
  } else {
    // non IE. give me DOMParser
    // theoretically this else branch should never be called
    // but just in case.
    xmlDoc = ( new DOMParser() ).parseFromString( s, "text/xml" );
  }
  return xmlDoc;
};

output.js

// Output the title of the loaded page
// And get the script-tags and output either the
// src attribute or code
function headerData(data) {
  // give me the head element
  var x = jQuery("head", data).eq(0);
  // output title
  alert(jQuery("title", x).eq(0).text());
  // for all scripttags which include a file out put src
  jQuery("script[src]", x).each(function(index) {
    alert((index+1)+" "+jQuery.attr(this, 'src'));
  });
  // for all scripttags which are inline javascript output code
  jQuery("script:not([src])", x).each(function(index) {
    alert(this.text);
  });
}

ready.js

$(document).ready(function() {
  $('#getit').click(function() {
    $.ajax({
      type : "GET",
      url : 'test.html',
      dataType : "xml",
      // overwrite content-type returned by server to ensure
      // the response getst treated as xml
      beforeSend: function(xhr) {
        // IE doesn't support this so check before using
        if (xhr.overrideMimeType) {
          xhr.overrideMimeType('text/xml');
        }
      },
      success: function(data) {
        headerData(data);
      },
      error : function(xhr, textStatus, errorThrown) {
        // if loading the response as xml failed try it manually
        // in theory this should only happen for IE
        // maybe some
        if (textStatus == 'parsererror') {
          var xmlDoc = jQuery.createXMLDocument(xhr.responseText);
          headerData(xmlDoc);
        } else {
          alert("Failed: " + textStatus + " " + errorThrown);
        }
      }
    });
  });
});

Dans Opera, le tout fonctionne sans le createXMLDocument y el beforeSend fonction.

L'astuce supplémentaire est nécessaire pour Firefox (3.0.11) et IE6 (je ne peux pas tester IE7, IE8, d'autres navigateurs) car ils ont un problème lorsque la balise Content-Type: renvoyée par le serveur n'indique pas qu'il s'agit de xml. Mon serveur web renvoie Content-Type: text/html; charset=UTF-8 pour test.html. Dans ces deux navigateurs, jQuery a appelé la fonction error avec textStatus en disant parsererror . Parce que dans la ligne 3706 dans jQuery.js

data = xml ? xhr.responseXML : xhr.responseText;

data est défini comme nul. Comme dans FF et IE, le xhr.responseXML est nulle. Cela se produit parce qu'ils ne comprennent pas que les données retournées sont xml (comme le fait Opera). Et seulement xhr.responseText est défini avec l'ensemble du code xhtml. Comme les données sont nulles, la ligne 3708

if ( xml && data.documentElement.tagName == "parsererror" )

lance une exception qui est rattrapée à la ligne 3584 et le statut est fixé à parsererror .

Dans FF, je peux résoudre le problème en utilisant la fonction overrideMimeType() avant d'envoyer la demande.

Mais IE ne prend pas en charge cette fonction sur l'objet XMLHttpRequest, je dois donc générer le document XML moi-même si la fonction de rappel d'erreur est exécutée et que l'erreur est la suivante parsererror .

exemple pour test.html

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Plugins | jQuery Plugins</title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">var imagePath = '/content/img/so/';</script>
  </head>
  <body>
  </body>
</html>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X