Question

Après un appel ajax jQuery pour récupérer un document XHTML complet, quel est le meilleur moyen de sélectionner des éléments spécifiques dans la chaîne résultante? Peut-être y a-t-il une bibliothèque ou un plugin qui résout ce problème?

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

Selon la documentation jQuery:

  

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

     

La chaîne HTML ne peut pas contenir   éléments non valides dans un   div, tel que html, tête, corps ou   éléments de titre.

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

Merci, Pete

Était-ce utile?

La solution

Au lieu de hacker jQuery pour ce faire, je vous suggérerais de vous retirer de jQuery pendant une minute et d’utiliser des méthodes dom XML brutes. En utilisant des méthodes XML Dom, vous pouvez le faire:

  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 problème avec les iframes, etc.

Autres conseils

Juste une idée - testée dans FF / Safari - semble fonctionner si vous créez un iframe pour stocker le document temporairement. Bien sûr, si vous faites cela, il serait peut-être plus judicieux d'utiliser simplement la propriété src de l'iframe pour charger le document et faire ce que vous voulez dans & "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 l'iframe au corps pour que celui-ci ait une .contentWindow.

Inspiré de cela répondre , 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 fumez-le avec:

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

LIVE DEMO (cliquez sur" Exécuter ")

Que diriez-vous de renommer rapidement les balises?

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

});

Cela fonctionne. Je viens de scinder les blocs de construction pour une meilleure lisibilité.

Vérifiez l'explication et les commentaires en ligne pour comprendre le fonctionnement de cette opération et la raison pour laquelle cela doit être fait de la sorte.

Bien sûr, cela ne peut pas être utilisé pour récupérer du contenu interdomaine car vous devez soit utiliser un proxy pour appeler les appels via votre script, soit envisager une intégration de type flXHR (Ajax interdomaine avec Flash)

call.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, tout fonctionne sans les fonctions createXMLDocument et beforeSend.

Cette astuce supplémentaire est nécessaire pour Firefox (3.0.11) et IE6 (impossible de tester IE7, IE8, les autres navigateurs) car ils rencontrent un problème lorsque le Content-Type: renvoyé par le serveur n'indique pas qu'il s'agit de xml. . Mon serveur Web a renvoyé Content-Type: text/html; charset=UTF-8 pour test.html. dans ces deux navigateurs, jQuery a appelé le rappel 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 sur null. Comme dans FF et IE, le xhr.responseXML est nul. Cela est dû au fait qu’ils ne comprennent pas que les données renvoyées sont au format XML (comme le fait Opera). Et seul xhr.responseText est défini avec le code xhtml complet. Comme les données sont nulles, la ligne 3708

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

lève une exception qui est interceptée à la ligne 3584 et dont le statut est défini sur overrideMimeType().

En FF, je peux résoudre le problème en utilisant la fonction <=> avant d'envoyer la requête.

Mais IE ne supporte pas cette fonction sur l'objet XMLHttpRequest, je dois donc générer moi-même le XMLDocument si le rappel d'erreur est exécuté et que l'erreur est <=>.

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>

Copié sans vergogne et adapté d'une autre de mes réponses ( Exemple jQuery simple avec ajax ne trouvant pas les éléments dans le code HTML renvoyé ), cela récupère le code HTML de la page distante, puis la fonction parseHTML crée un élément div temporaire pour celui-ci et place le lot à l'intérieur, qu'il parcourt et retourne l’élément demandé. jQuery alerte ensuite 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 obtenir plusieurs éléments ou une liste de balises de script, supposons que vous deviez améliorer la fonction parseHTML, mais bon - preuve du concept: -)

Si vous vouliez trouver la valeur de champs nommés spécifiquement (c'est-à-dire les entrées d'un formulaire), 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();
    ....
$.get('yourpage.html',function(data){
    var content = $('<div/>').append(data).find('#yourelement').html();
});

Vous pouvez aussi simplement envelopper temporairement dans un div. Vous n'avez même pas besoin de l'ajouter au DOM.

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top