Pergunta

Depois de uma chamada ajax jQuery para recuperar um documento XHTML inteiro, o que é a melhor maneira de selecionar elementos específicos da cadeia resultante? Talvez haja uma biblioteca ou um plugin que resolve este problema?

jQuery só pode selecionar elementos XHTML que existem em uma corda se eles estão normalmente permitido em uma div na especificação W3C; portanto, estou curioso sobre como selecionar coisas como <title>, <script> e <style>.

De acordo com a documentação do jQuery:

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

A seqüência de HTML não pode conter elementos que são inválidos dentro de um div, como html, cabeça, corpo, ou elementos do título.

Portanto, uma vez que nós estabelecemos que jQuery não fornece uma maneira de fazer isso, como é que eu selecionar esses elementos? Como exemplo, se você pode me mostrar como selecionar o título da página remota, que seria perfeito!

Obrigado, Pete

Foi útil?

Solução

Em vez de cortar jQuery para fazer isso eu sugiro que você cair fora da jQuery por um minuto e usar métodos DOM XML brutos. Usando XML métodos DOM que você pode fazer isso:

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

Sem mexer com iframe etc.

Outras dicas

Apenas uma idéia - testado em FF / Safari - parece funcionar se você criar um iframe para armazenar o documento temporariamente. Claro, se você estiver fazendo isso, pode ser mais inteligente para usar apenas a propriedade src do iframe para carregar o documento e fazer o que quiser no "onload" do mesmo.

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

Eu tive que acrescentar o iframe para o corpo para que ele comece a ter um .contentWindow.

Inspirado de este responder , mas com diferida:

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

E fumá-lo com:

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

Demonstração ao vivo (clique em 'Run')

Que tal um pouco de renomeação tag rápida?

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

});

Isso funciona. Eu só dividir os blocos de construção para melhor legibilidade.

Verifique a explicação e os comentários incluídos para compreender o funcionamento deste e por isso tem de ser feito assim.

Claro que isso não pode ser usado para recuperar-domain-content cruz por que você quer ter de procuração as chamadas através de um script de seu ou pensar em algo integração como flXHR (Ajax entre domínios com 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 é (jQuery 1.3.2 não comprimido) test.html a válido 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;
};

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

Em Opera a coisa toda funciona sem a createXMLDocument ea função beforeSend.

O truque extra é necessário para o Firefox (3.0.11) e IE6 (não pode testar IE7, IE8, outros navegadores) como eles têm um problema quando o Content-Type: retornado pelo servidor não indica que é xml. Meu servidor web retornou Content-Type: text/html; charset=UTF-8 para test.html. Nesses dois navegadores jQuery chamada de retorno de chamada error com textStatus dizendo parsererror. Porque na linha 3706 em jquery.js

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

data está sendo definido como nulo. Como em FF e IE o xhr.responseXML é nulo. Isso acontece porque eles não conseguir que os dados retornados é xml (como Opera faz). E só xhr.responseText é definido com todo o código xhtml. Como os dados é nulo da linha 3708

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

lança uma exceção que é catched na linha 3584 e status é definido como parsererror.

Em FF eu posso resolver o problema usando a função overrideMimeType() antes de enviar a solicitação.

Mas IE não suporta essa função no XMLHttpRequest-objeto então eu tenho que gerar o XMLDocument mim mesmo se o erro de retorno de chamada é executado eo erro é parsererror.

exemplo para 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>

Shamelessly copiado e adaptado de outra das minhas respostas ( exemplo simples jQuery ajax não encontrar elementos voltou HTML ), este busca o HTML da página remota, então a função parseHTML cria um elemento div temporária para ele e coloca o interior muito, percorre-lo e retorna o elemento solicitado. jQuery, em seguida, alerta o texto interno ().

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

Para obter diversos itens fora ou uma lista de tags de script, por exemplo, eu acho que você tem para melhorar a função parseHTML, mas hey - prova de conceito: -)

Se você quiser encontrar o valor de campos especificamente nomeado (isto é, as entradas em um formulário) algo como isto iria encontrá-los para você:

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

function findFields(form, fields) {
  var form = $(form);
  fields.forEach(function(field) {
    var val = form.find("[name="+field+"]").val();
    ....

Como sobre este: XML Load from corda

$.get('yourpage.html',function(data){
    var content = $('<div/>').append(data).find('#yourelement').html();
});

Você também pode simplesmente envolver temporariamente dentro de uma div. Você não precisa mesmo de adicioná-la à DOM.

Depois de analisar a seqüência de caracteres XML em um XML DOM, eu quer jQuery uso nele diretamente (você pode fazer isso, fornecendo um contexto para o seletor jQUery, como $(':title', xdoc.rootElement) ou usando XPath (funciona no Firefox, há supostamente bibliotecas para o IE, mas eu não tive um bom sucesso com eles).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top