Voici quelque chose qui me pose un peu de difficulté. J'ai un script local côté client qui doit permettre à un utilisateur d'extraire une page Web distante et de rechercher des formulaires dans cette page résultante. Pour ce faire (sans regex), je dois analyser le document dans un objet DOM totalement traversable.
Quelques limites sur lesquelles j'aimerais insister:
getElementsByTagName
, doivent être disponibles.En supposant que j'ai une chaîne de document HTML complète (y compris la déclaration DOCTYPE) dans la variable html
, voici ce que j'ai déjà essayé:
var frag = document.createDocumentFragment(),
div = frag.appendChild(document.createElement("div"));
div.outerHTML = html;
//-> results in an empty fragment
div.insertAdjacentHTML("afterEnd", html);
//-> HTML is not added to the fragment
div.innerHTML = html;
//-> Error (expected, but I tried it anyway)
var doc = new ActiveXObject("htmlfile");
doc.write(html);
doc.close();
//-> JavaScript executes
J'ai également essayé d'extraire les codes <head>
et <body>
du code HTML et de les ajouter à un élément <HTML>
dans le fragment, toujours pas de chance.
Quelqu'un a-t-il une idée?
Vous ne savez pas pourquoi vous manipulez documentFragments, vous pouvez simplement définir le texte HTML comme la variable innerHTML
d'un nouvel élément div. Ensuite, vous pouvez utiliser cet élément div pour getElementsByTagName
etc. sans ajouter le div à DOM:
var htmlText= '<html><head><title>Test</title></head><body><div id="test_ele1">this is test_ele1 content</div><div id="test_ele2">this is test_ele content2</div></body></html>';
var d = document.createElement('div');
d.innerHTML = htmlText;
console.log(d.getElementsByTagName('div'));
Si vous êtes vraiment marié à l'idée d'un documentFragment, vous pouvez utiliser ce code, mais vous devrez quand même l'envelopper dans un div pour obtenir les fonctions DOM recherchées
function makeDocumentFragment(htmlText) {
var range = document.createRange();
var frag = range.createContextualFragment(htmlText);
var d = document.createElement('div');
d.appendChild(frag);
return d;
}
Je ne sais pas si IE supporte document.implementation.createHTMLDocument
, mais si c'est le cas, utilisez cet algorithme (adapté de mon extension HTML DOMParser ). Notez que le DOCTYPE ne sera pas conservé .:
var
doc = document.implementation.createHTMLDocument("")
, doc_elt = doc.documentElement
, first_elt
;
doc_elt.innerHTML = your_html_here;
first_elt = doc_elt.firstElementChild;
if ( // are we dealing with an entire document or a fragment?
doc_elt.childElementCount === 1
&& first_elt.tagName.toLowerCase() === "html"
) {
doc.replaceChild(first_elt, doc_elt);
}
// doc is an HTML document
// you can now reference stuff like doc.title, etc.
En supposant que le code HTML soit également valide, vous pouvez utiliser loadXML ()
Utiliser des capacités DOM HTML complètes sans déclencher de requêtes, sans avoir à gérer d'incompatibilités:
var doc = document.cloneNode();
if (!doc.documentElement) {
doc.appendChild(doc.createElement('html'));
doc.documentElement.appendChild(doc.createElement('head'));
doc.documentElement.appendChild(doc.createElement('body'));
}
Tous ensemble! doc est un document html, mais il n'est pas en ligne.
DocumentFragment
ne supporte pas getElementsByTagName
- cela n'est supporté que par Document
.
Vous devrez peut-être utiliser une bibliothèque comme jsdom , qui fournit une implémentation du DOM et dans laquelle vous pouvez effectuer une recherche à l'aide de getElementsByTagName
et d'autres API DOM. Et vous pouvez le configurer pour ne pas exécuter de scripts. Oui, c'est «lourd» et je ne sais pas si cela fonctionne dans IE 7.
Je me suis promené sur cette page, je suis un peu en retard pour être utile :) mais ce qui suit devrait aider toute personne confrontée à un problème similaire à l'avenir ... cependant IE7/8 devrait vraiment être ignoré maintenant et il existe de bien meilleures méthodes supportées par les navigateurs plus modernes.
Les travaux suivants sur presque tout ce que j'ai testé - les deux seuls inconvénients sont:
J'ai ajouté des fonctions getElementById
et getElementsByName
sur mesure à l'élément div de racine, afin qu'elles n'apparaissent pas comme prévu dans l'arborescence (sauf si le code est modifié pour répondre à cela) .
Le doctype sera ignoré - mais je ne pense pas que cela fera beaucoup de différence car mon expérience est que le doctype n'aura aucune incidence sur la structure du dom, ni sur la manière dont il est rendu (ce qui ne se produira évidemment pas avec cette méthode) .
En gros, le système repose sur le fait que <tag>
et <namespace:tag>
sont traités différemment par les agents utilisateurs. Comme il a été constaté, certaines balises spéciales ne peuvent pas exister dans un élément div et sont donc supprimées. Les éléments namespaced peuvent être placés n'importe où (sauf indication contraire d'une DTD) . Bien que ces balises d'espace de noms ne se comportent pas réellement comme les vraies balises en question, étant donné que nous ne les utilisons réellement que pour leur position structurelle dans le document, cela ne pose pas vraiment de problème.
le balisage et le code sont les suivants:
<!DOCTYPE html>
<html>
<head>
<script>
/// function for parsing HTML source to a dom structure
/// Tested in Mac OSX, Win 7, Win XP with FF, IE 7/8/9,
/// Chrome, Safari & Opera.
function parseHTML(src){
/// create a random div, this will be our root
var div = document.createElement('div'),
/// specificy our namespace prefix
ns = 'faux:',
/// state which tags we will treat as "special"
stn = ['html','head','body','title'];
/// the reg exp for replacing the special tags
re = new RegExp('<(/?)('+stn.join('|')+')([^>]*)?>','gi'),
/// remember the getElementsByTagName function before we override it
gtn = div.getElementsByTagName;
/// a quick function to namespace certain tag names
var nspace = function(tn){
if ( stn.indexOf ) {
return stn.indexOf(tn) != -1 ? ns + tn : tn;
}
else {
return ('|'+stn.join('|')+'|').indexOf(tn) != -1 ? ns + tn : tn;
}
};
/// search and replace our source so that special tags are namespaced
/// required for IE7/8 to render tags before first text found
/// <faux:check /> tag added so we can test how namespaces work
src = ' <'+ns+'check />' + src.replace(re,'<$1'+ns+'$2$3>');
/// inject to the div
div.innerHTML = src;
/// quick test to see how we support namespaces in TagName searches
if ( !div.getElementsByTagName(ns+'check').length ) {
ns = '';
}
/// create our replacement getByName and getById functions
var createGetElementByAttr = function(attr, collect){
var func = function(a,w){
var i,c,e,f,l,o; w = w||[];
if ( this.nodeType == 1 ) {
if ( this.getAttribute(attr) == a ) {
if ( collect ) {
w.Push(this);
}
else {
return this;
}
}
}
else {
return false;
}
if ( (c = this.childNodes) && (l = c.length) ) {
for( i=0; i<l; i++ ){
if( (e = c[i]) && (e.nodeType == 1) ) {
if ( (f = func.call( e, a, w )) && !collect ) {
return f;
}
}
}
}
return (w.length?w:false);
}
return func;
}
/// apply these replacement functions to the div container, obviously
/// you could add these to prototypes for browsers the support element
/// constructors. For other browsers you could step each element and
/// apply the functions through-out the node tree... however this would
/// be quite messy, far better just to always call from the root node -
/// or use div.getElementsByTagName.call( localElement, 'tag' );
div.getElementsByTagName = function(t){return gtn.call(this,nspace(t));}
div.getElementsByName = createGetElementByAttr('name', true);
div.getElementById = createGetElementByAttr('id', false);
/// return the final element
return div;
}
window.onload = function(){
/// parse the HTML source into a node tree
var dom = parseHTML( document.getElementById('source').innerHTML );
/// test some look ups :)
var a = dom.getElementsByTagName('head'),
b = dom.getElementsByTagName('title'),
c = dom.getElementsByTagName('script'),
d = dom.getElementById('body');
/// alert the result
alert(a[0].innerHTML);
alert(b[0].innerHTML);
alert(c[0].innerHTML);
alert(d.innerHTML);
}
</script>
</head>
<body>
<xmp id="source">
<!DOCTYPE html>
<html>
<head>
<!-- Comment //-->
<meta charset="utf-8">
<meta name="robots" content="index, follow">
<title>An example</title>
<link href="test.css" />
<script>alert('of parsing..');</script>
</head>
<body id="body">
<b>in a similar way to createDocumentFragment</b>
</body>
</html>
</xmp>
</body>
</html>