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

suite à un appel jQuery ajax pour récupérer un document entier XHTML, Quelle est la meilleure façon de sélectionner des éléments spécifiques à partir de la chaîne résultante? Peut-être qu'il y a 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 une div dans la spécification W3C; par conséquent, je suis curieux de sélectionner des choses comme <title> , <script> , et <style> .

Selon la documentation de jQuery:

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

la chaîne HTML ne peut pas contenir les éléments invalides dans un div, comme html, head, body, or titre des éléments.

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

Merci, Pete

34
demandé sur slypete 2009-06-24 00:10:57

10 réponses

au lieu de hacker jQuery pour faire ça, je vous suggère de laisser tomber jQuery pendant une minute et d'utiliser des méthodes de dom XML brutes. En utilisant les méthodes Dom XML 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 de vous embêter avec les iframes etc.

28
répondu David Burrows 2009-07-01 15:52:40

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

  $(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 l'iframe au corps pour qu'il ait un .contentWindow.

5
répondu gnarf 2014-12-23 00:58:02

inspiré de cette réponse , mais avec différé:

function fetchDoc(url) {
  var dfd;
  dfd = $.Deferred();

  $.get(url).done(function (data, textStatus, jqXHR) {

    var $iframe = $('<iframe style="display:none;"/>').appendTo('body');
    var $doc = $iframe.contents();
    var doc = $doc[0];

    $iframe.load(function() {
      dfd.resolveWith(doc, [data, textStatus, jqXHR]);
      return $iframe.remove();
    });
    doc.open();
    doc.write(data);

    return doc.close();
  }).fail(dfd.reject);

  return dfd.promise();
};

et le fumer avec:

fetchDoc('/foo.html').done(function (data, textStatus, jqXHR) {
  alert($('title', this).text());
});

LIVE DEMO (click 'Run')

3
répondu abernier 2017-05-23 12:31:59

Que dirais-tu d'un changement de nom?

$.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());
    }

});
2
répondu Ben Koehler 2009-06-26 14:24:13

ça marche. Je viens de séparer les blocs de construction pour une meilleure lisibilité.

Vérifiez l'explication et les commentaires en ligne pour saisir le fonctionnement de ceci et pourquoi il doit être fait comme ceci.

bien sûr, cela ne peut pas être utilisé pour récupérer le contenu cross-domain pour que vous devez soit proxy les appels à travers un script de la vôtre ou penser à l'intégration quelque chose comme flXHR (Cross-Domain Ajax with 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 valide XHTML-Document

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;
};

sortie.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);
  });
}

prêt.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);
        }
      }
    });
  });
});

À l'Opéra le tout fonctionne sans le createXMLDocument et le beforeSend de la fonction.

L'astuce supplémentaire est nécessaire pour Firefox (3.0.11) et IE6 (ne peut pas tester IE7, IE8, autres navigateurs) car ils ont un problème quand le Content-Type: retourné par le serveur n'indique pas que c'est xml. Mon serveur web a retourné Content-Type: text/html; charset=UTF-8 pour test.html. dans ces deux navigateurs jQuery appelé le error callback avec textStatus disant parsererror . Parce qu'à la ligne 3706 à jQuery.js

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

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

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

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

en FF I peut résoudre le problème en utilisant la fonction overrideMimeType() avant d'envoyer la requête.

mais IE ne supporte pas cette fonction sur L'objet XMLHttpRequest, donc je dois générer le XMLDocument moi-même si la fonction error-callback est lancée et que l'erreur est parsererror .

exemple d'essai.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>
2
répondu jitter 2009-06-29 09:57:05

sans vergogne copié et adapté d'une autre de mes réponses ( simple jQuery ajax exemple ne trouvant pas d'éléments dans le HTML retourné ), cela récupère le HTML de la page distante, puis la fonction parseHTML crée un élément div temporaire pour lui et met le lot à l'intérieur, l'exécute, et renvoie l'élément demandé. jQuery alerte alors le texte() à l'intérieur.

$(document).ready(function(){
  $('input').click(function(){
    $.ajax({
      type : "POST",
      url : 'ajaxtestload.html',
      dataType : "html",
      success: function(data) {
        alert( data ); // shows whole dom
        var gotcha = parseHTML(data, 'TITLE'); // nodeName property returns uppercase
        if (gotcha) {
          alert($(gotcha).html()); // returns null
        }else{
          alert('Tag not found.');
        }
      },
      error : function() {
        alert("Sorry, The requested property could not be found.");
      }
    });
  });
});

function parseHTML(html, tagName) {
  var root = document.createElement("div");
  root.innerHTML = html;
  // Get all child nodes of root div
  var allChilds = root.childNodes;
  for (var i = 0; i < allChilds.length; i++) {
    if (allChilds[i].nodeName == tagName) {
      return allChilds[i];
    }
  }
  return false;
}

pour sortir plusieurs éléments ou une liste de balises de script, disons, je pense vous auriez à améliorer la fonction parseHTML, mais hey-proof of concept: -)

1
répondu MSpreij 2017-05-23 12:31:59

si vous vouliez trouver la valeur des champs spécifiquement nommés (c.-à-d. les entrées dans une forme) quelque chose comme ceci les trouverait pour vous:

var fields = ["firstname","surname", ...."foo"];

function findFields(form, fields) {
  var form = $(form);
  fields.forEach(function(field) {
    var val = form.find("[name="+field+"]").val();
    ....
0
répondu timbo 2009-06-23 21:06:44

Que Diriez-vous de ceci: chargez XML à partir de la chaîne

0
répondu Svante 2009-06-24 01:40:19
$.get('yourpage.html',function(data){
    var content = $('<div/>').append(data).find('#yourelement').html();
});

vous pouvez aussi simplement envelopper temporairement à l'intérieur d'un div. Tu n'as même pas besoin de l'ajouter au DOM.

0
répondu Paul 2012-09-17 14:19:33

après avoir analysé la chaîne XML dans un XML DOM , j'utiliserais jQuery directement dessus (vous pouvez le faire en fournissant un contexte au sélecteur jQUery , tel que $(':title', xdoc.rootElement) ou en utilisant XPath (fonctionne dans Firefox; il y aurait des bibliothèques pour IE mais je n'ai pas eu de bon succès avec eux).

0
répondu Michael Ekstrand 2015-08-24 13:49:17