Grundsätzlich habe ich ein Textfeld, in das ich die URL eingebe und auf "OK" klicke. Es zeigt eine Vorschau von HTML auf der linken Seite. Die rechte Seite enthält eine Baumansicht der HTML-Tags (body, header, div, span usw.), die in HTML als angefügtes Bild verwendet werden. Das erwartete JSON-Ergebnis sollte am Ende dieser Frage stehen. Ich kann JSON nicht durchlaufen und keinen Baum erstellen. Ich habe folgendes versucht:
HTML- und JS-Code:
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ABC</title>
<link rel="stylesheet" type="text/css" href="css/main.css" />
</head>
<body>
<div id="wrapper">
<header>
<h1 class="logo"><img src="images/logo.png" alt="" title="" /></h1>
</header>
<div id="container">
<div class="search-box">
<input type="text" id="url" value="" class="txt-box" />
<input type="button" value="OK" class="btn-search" />
</div>
<div class="inner-wrap">
<div class="left-wrap" id="preview-sec">
</div>
<div class="right-wrap" id="tree-sec">
</div>
</div>
</div>
</div>
<script type="text/javascript" language="javascript" src="js/jquery-1.11.1.js"></script><!-- Jquery plugin -->
<script>
var counter = 0;
$(document).ready(function(){
$('.btn-search').click(function(){
if ($('#url').val() != '') {
$.get(
'http://localhost/test/getHTML.php', {url:$('#url').val()},
function(response) {
$('#preview-sec').html(response);
},'html');
$.getJSON('http://localhost/test/results.json', function(json) {
traverse(json,0);
});
}
});
});
function traverse(obj,id){
if (typeof(obj)=="object") {
if (id == 0) {
$('#tree-sec').append('<ul></ul>');
} else {
$(id).append('<ul></ul>');
}
$.each(obj, function(i,val){
if (i != 'attributes' && i != 'value') {
counter += 1;
var li_populate = "<li id="+i+"-"+counter+">"+i+"</li>";
if (id == 0) {
$('#tree-sec ul').append(li_populate);
} else {
$(id).find('ul').append(li_populate);
}
traverse(val,"#"+i+"-"+counter);
}
})
}
}
</script>
</body>
</html>
PHP Code:
<?php
$url = $_GET['url'];
$html = file_get_contents($url);
function html_to_obj($html) {
$dom = new DOMDocument();
$dom->loadHTML($html);
return element_to_obj($dom->documentElement);
}
function element_to_obj($element) {
//print_r($element);
$obj = array();
$attr = array();
$arr = array();
$name = $element->tagName;
foreach ($element->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
if ($attribute->name == 'id') {
$name .= '#'.$attribute->value;
}
}
if (!empty($attr)) {
$arr["attributes"] = $attr;
}
if ($element->nodeValue != '') {
$arr["value"] = $element->nodeValue;
}
foreach ($element->childNodes as $subElement) {
if ($subElement->nodeType == XML_TEXT_NODE) {
}
elseif ($subElement->nodeType == XML_CDATA_SECTION_NODE) {
}
else {
$arr["child_nodes"][] = element_to_obj($subElement);
}
}
$obj[$name] = $arr;
return $obj;
}
$json = json_encode(html_to_obj($html));
$fp = fopen('results.json', 'w');
fwrite($fp,$json);
fclose($fp);
echo $html;exit();
?>
JSON-Baumausgabe:
JSON-Ergebnis:
{
"html": {
"attributes": {
"lang": "en"
},
"value": "Test Development Test\r\n *{\r\n box-sizing:border-box;\r\n }\r\n body {\r\n margin:0;\r\n font-family: sans-serif;\r\n color: #999;\r\n }\r\n a, a:visited {\r\n text-decoration:none;\r\n }\r\n .movie-list .movie{\r\n width:250px;\r\n float:left;\r\n margin-right:25px;\r\n }\r\n .movie-list .movie img{\r\n width:100%;\r\n }\r\n .movie-list .movie a.title{\r\n text-decoration:none;\r\n color:#999;\r\n font-weight:bold;\r\n font-size:18px;\r\n line-height:25px;\r\n }\r\n .movie-list .movie .synopsis{\r\n font-size:14px;\r\n line-height:20px;\r\n }\r\n",
"child_nodes": {
"head": {
"child_nodes": {
"meta": {
"attributes": {
"name": "description",
"content": "A ast of animated movies"
}
},
"title": {
"value": "Test Development Test"
},
"style": {
"attributes": {
"type": "text/css"
},
"value": "\r\n *{\r\n box-sizing:border-box;\r\n }\r\n body {\r\n margin:0;\r\n font-family: sans-serif;\r\n color: #999;\r\n }\r\n a, a:visited {\r\n text-decoration:none;\r\n }\r\n .movie-list .movie{\r\n width:250px;\r\n float:left;\r\n margin-right:25px;\r\n }\r\n .movie-list .movie img{\r\n width:100%;\r\n }\r\n .movie-list .movie a.title{\r\n text-decoration:none;\r\n color:#999;\r\n font-weight:bold;\r\n font-size:18px;\r\n line-height:25px;\r\n }\r\n .movie-list .movie .synopsis{\r\n font-size:14px;\r\n line-height:20px;\r\n }\r\n"
}
}
},
"body": {
"child_nodes": {
"h1": {
"value": "List of animated movies"
},
"div": {
"attributes": {
"class": "movie-list"
},
"child_nodes": {
"div#bh_6": {
"attributes": {
"class": "movie",
"id": "bh_6",
"data-year": "2014"
},
"child_nodes": {
"img": {
"attributes": {
"src": "http://ia.media-imdb.com/images/M/[email protected]_V1_SY317_CR0,0,214,317_AL_.jpg"
}
},
"a": {
"attributes": {
"class": "title",
"href": "http://www.imdb.com/title/tt2245084/"
},
"value": "Big Hero 6"
},
"div": {
"attributes": {
"class": "synopsis"
},
"value": "The special bond that develops between plus-sized inflatable robot Baymax, and prodigy Hiro Hamada, who team up with a group of friends to form a band of high-tech heroes."
}
}
},
"div#tlm": {
"attributes": {
"class": "movie",
"id": "tlm",
"data-year": "2014"
},
"child_nodes": {
"img": {
"attributes": {
"src": "http://ia.media-imdb.com/images/M/[email protected]_V1_SX214_AL_.jpg"
}
},
"a": {
"attributes": {
"class": "title",
"href": "http://www.imdb.com/title/tt1490017/"
},
"value": "The Lego Movie"
},
"div": {
"attributes": {
"class": "synopsis"
},
"value": "An ordinary Lego construction worker, thought to be the prophesied 'Special', is recruited to join a quest to stop an evil tyrant from gluing the Lego universe into eternal stasis."
}
}
},
"div#httyd": {
"attributes": {
"class": "movie",
"id": "httyd",
"data-year": "2010"
},
"child_nodes": {
"img": {
"attributes": {
"src": "http://ia.media-imdb.com/images/M/[email protected]@._V1_SX214_AL_.jpg"
}
},
"a": {
"attributes": {
"class": "title",
"href": "http://www.imdb.com/title/tt0892769/"
},
"value": "How to Train Your Dragon"
},
"div": {
"attributes": {
"class": "synopsis"
},
"value": "A hapless young Viking who aspires to hunt dragons becomes the unlikely friend of a young dragon himself, and learns there may be more to the creatures than he assumed."
}
}
},
"div#up": {
"attributes": {
"class": "movie",
"id": "up",
"data-year": "2009"
},
"child_nodes": {
"img": {
"attributes": {
"src": "http://ia.media-imdb.com/images/M/[email protected]_V1_SX214_AL_.jpg"
}
},
"a": {
"attributes": {
"class": "title",
"href": "http://www.imdb.com/title/tt1049413/"
},
"value": "Up"
},
"div": {
"attributes": {
"class": "synopsis"
},
"value": "By tying thousands of balloons to his home, 78-year-old Carl sets out to fulfill his lifelong dream to see the wilds of South America. Russell, a wilderness Explorer 70 years younger, inadvertently becomes a stowaway."
}
}
},
"div#mi": {
"attributes": {
"class": "movie",
"id": "mi",
"data-year": "2001"
},
"child_nodes": {
"img": {
"attributes": {
"src": "http://ia.media-imdb.com/images/M/[email protected]_V1_SX214_AL_.jpg"
}
},
"a": {
"attributes": {
"class": "title",
"href": "http://www.imdb.com/title/tt0198781/"
},
"value": "Monsters, Inc."
},
"div": {
"attributes": {
"class": "synopsis"
},
"value": "Monsters generate their city's power by scaring children, but they are terribly afraid themselves of being contaminated by children, so when one enters Monstropolis, top scarer Sulley finds his world disrupted."
}
}
}
}
}
}
}
}
}
}
Gemäß Ihrer Frage ist der Teil, in dem Sie das zurückgegebene JSON-Objekt durchlaufen und den Baum erstellen, problematisch. In Ihrem Code hatte die rekursive Funktion zum Durchlaufen der JSON-Daten einige kleinere Probleme mit dem Code generateul
name__. Die Struktur des Rückgabeobjekts machte es etwas herausfordernd.
Ich konnte Ihren html/javascript
Code ein wenig ändern (ohne ihn zu stark zu ändern), um den Baum auszudrucken. Der relevante Code unten:
CSS:
div#tree-sec ul ul{
margin-left: 25px;
}
div#tree-sec ul li{
color: #666;
}
div#tree-sec ul a{
color: #111;
text-decoration: underline;
cursor: pointer;
}
div#tree-sec ul a:hover {
text-decoration: none;
}
div#tree-sec ul.collapsible{
/* Custom parent styles here... */
/* Such as a folder icon or 'plus' sign */
}
HTML & JS:
...
...
<div class="inner-wrap">
<div class="left-wrap" id="preview-sec">
<iframe id="preview"></iframe>
</div>
<div class="right-wrap" id="tree-sec">
<ul id="treehtml1"></ul>
</div>
</div>
....
....
<script type="text/javascript">
var counter = 0;
$(document).ready(function(){
$('.btn-search').click(function(){
if ($('#url').val() != '') {
$.get('http://localhost/test/getHTML.php', {url:$('#url').val()}, function(response) {
$('#preview-sec').html(response);
},'html');
$.getJSON('http://localhost/test/results.json', function(json) {
if(typeof(json) == "object"){
traverse(json,'html',1);
makeCollapsible();
}
});
}
});
});
function traverse(obj, element, counter){
for (var i in obj){
$("#tree"+element+counter).append("<li id='"+i+counter+"'>"+i+"</li>"); // Add element to the tree
if(obj[i].hasOwnProperty('child_nodes')){
$("#"+i+counter).append("<ul id='tree"+i+(counter+1)+"'></ul>"); // If there are children, add a parent ul and pass the name to subsequent recursive calls for each child
for(var j in obj[i].child_nodes){
traverse(obj[i].child_nodes[j], i, counter + 1); // Recursive call to add child
}
}
}
}
function makeCollapsible(){
$('ul.parent').each(function(i) {
var parent_li = $(this).parent('li');
parent_li.addClass('collapsible'); //Use this selector to style your parent items...
// Temporarily remove the list from the
// parent list item, wrap the remaining
// text in an anchor, then reattach it.
var sub_ul = $(this).remove();
parent_li.wrapInner('<a/>').children('a').click(function() {
// Toggle the children...
sub_ul.toggle();
});
parent_li.append(sub_ul);
});
// Hide all lists except the outermost.
$('ul ul').hide();
}
</script>
....
....
Dies sollte einen ordnungsgemäß verschachtelten ul
name__-basierten Baum liefern. Wenn das Erstellen eines Abbilds des Baums eine schwierige Aufgabe ist, sollten Sie das generierte Codefragment ul
korrekt formatieren, eine HTML-Seite damit auf dem Server erstellen und dann ein serverseitiges Tool wie wkhtmltoimage aus dem verwenden wkhtmltopdf package das verwendet werden kann, um das HTML-Dokument in ein Bild zu rendern.
Eine andere Sache, die ich erwähnen möchte, ist, dass ich, anstatt das abgerufene HTML in ein Div zu laden, empfehlen würde, dass Sie einen iframe
verwenden, da das abgerufene HTML dann Ihre aktuelle Seite nicht stören würde. In meinem obigen Beispiel habe ich der Vorschau iframe
einen div
hinzugefügt. In einem solchen Fall können Sie php verwenden, um nur die json
-Daten auszugeben, und das Festlegen der iframe
für die Vorschau der URL ist so einfach wie das Zuweisen der URL wie das Attribut src
des iframe
name__. So: $("#preview").prop("src", $("#url").val())
.
Bearbeiten : Der Code wurde mit einem Fix aktualisiert. Es wurde auch eine neue js-Funktion makeCollapsible()
hinzugefügt, um die ul
nachträglich in eine anklickbare, zusammenklappbare Baumstruktur zu konvertieren, wie im Kommentar von OP angegeben. Außerdem wurden relevante CSS
-Stile hinzugefügt, um die Baumstruktur zu formatieren. Der Baum sieht für mich jetzt so aus:
Nachtrag: Dies ist eine lange Antwort, die jedoch spezifische Probleme und Lösungen für die von Ihnen bereitgestellten Code-Schnipsel anspricht. Ich hoffe, Sie und andere finden es die Zeit wert zu vergleichen. :)
Beim Parsen des DOM empfehle ich, die Elementnamen aus dem zurückgegebenen Objekt als assoziative Schlüssel in $arr['child_nodes']
mithilfe von array_merge()
festzulegen, anstatt sie als indizierte Elemente auf das Array zu übertragen. Dazu muss $arr['child_nodes']
zuerst als Array definiert werden. Später, wenn keine Elemente darin zusammengeführt werden, unset
, bevor $arr
zum Hauptobjekt hinzugefügt wird.
Dies vereinfacht das Parsen des endgültigen JSON-Ergebnisses, da beim Erstellen des Baums keine verschachtelte Schleife in Ihrem Javascript erforderlich ist.
Ich empfehle außerdem, bedingte Prüfungen für ->length
einzufügen, bevor foreach
-Schleifen ausgeführt werden. Ihr vorhandener Code hat Warnmeldungen ausgegeben, als Elemente mit der Länge Null in die Schleife eingegeben wurden.
Zuletzt können Sie Ihre Logik für den Umgang mit Knotentypen vereinfachen, indem Sie Ihre aktuelle if, else if, else
-Anweisung durch eine einzelne if
-Überprüfung für $subElement->nodeType === XML_ELEMENT_NODE
ersetzen. Ich denke, Sie möchten dies erreichen.
<?php
$url = $_GET['url'];
$html = file_get_contents($url);
function html_to_obj($html) {
$dom = new DOMDocument();
$dom->loadHTML($html);
return element_to_obj($dom->documentElement);
}
function element_to_obj($element) {
$obj = $attr = $arr = array();
$name = $element->tagName;
if ($element->attributes->length) {
foreach ($element->attributes as $attribute) {
$attr[$attribute->name] = $attribute->value;
if ($attribute->name == 'id') {
$name .= '#'.$attribute->value;
}
}
}
if (!empty($attr)) {
$arr["attributes"] = $attr;
}
if ($element->nodeValue != '') {
$arr["value"] = $element->nodeValue;
}
if ($element->childNodes->length) {
$arr["child_nodes"] = array();
foreach ($element->childNodes as $subElement) {
if ($subElement->nodeType === XML_ELEMENT_NODE) {
$arr["child_nodes"] = array_merge($arr["child_nodes"], element_to_obj($subElement));
}
}
if (!count($arr["child_nodes"])) {
unset($arr["child_nodes"]);
}
}
$obj[$name] = $arr;
return $obj;
}
$json = json_encode(html_to_obj($html));
$fp = fopen('results.json', 'w');
fwrite($fp, $json);
fclose($fp);
?>
Fügen Sie einen leeren iframe ein, in den Sie Ihre Zielsite laden. Das Einfügen des Markups von einer anderen Site in Ihre Site kann (und wird wahrscheinlich) Konflikte mit Ihrem eigenen Code verursachen.
<div class="inner-wrap">
<div class="left-wrap" id="preview-sec">
<iframe src=""></iframe>
</div>
<div class="right-wrap" id="tree-sec">
</div>
</div>
Die Funktion traverse
hatte drei Fehler:
Die Verwendung eines Zählers, um IDs im laufenden Betrieb zu erstellen und anschließend mithilfe von jQuery zuvor erstellte Elemente mit den IDs zu finden, an die Listenelemente angehängt werden, war ein Leistungsverlust und verwirrend für das Debugging.
Die Verwendung von .find()
führte dazu, dass jQuery redundant in den rekursiven Aufruf eintrat und dem Baum mehrfach redundante untergeordnete Knoten hinzufügte.
Da es sich um den Rückruf eines separaten asynchronen Aufrufs handelt, kann er ausgeführt werden, bevor der erste asynchrone Aufruf von getHTML.php
beendet wurde.
Verschieben Sie den asynchronen Aufruf, um den JSON-Code beim ersten asynchronen Aufruf in die Rückruffunktion zu versetzen und zu verhindern, dass unvollständiger oder alter JSON-Code vom Server abgerufen wird.
Sie sollten diesen ersten Rückruf auch verwenden, um iframe
src
festzulegen und den Container #tree-sec
zu leeren, damit nachfolgende Aktionen nicht mehr als einen Baum anhängen. Sie können dasselbe erreichen, indem Sie .replace()
anstelle von .empty()
gefolgt von .append()
verwenden.
Um den Baum zu erstellen, empfehle ich den folgenden einfacheren Ansatz, bei dem die Liste rekursiv als Zeichenfolge erstellt wird, sodass die .append
-Methode nur einmal aufgerufen wird. Bei größeren Bäumen wird dadurch die Leistung erheblich verbessert.
Sie können dieser Funktion einen Zähler und dynamisch zugewiesene IDs hinzufügen, wenn Sie möchten, aber ich habe darauf verzichtet, um klarer zu zeigen, dass sie nicht zum Erstellen des Baums benötigt werden.
Ich empfehle außerdem, vor dem Eingeben eines rekursiven Aufrufs zu prüfen, ob untergeordnete Knoten vorhanden sind. Bei dieser Prüfung können Sie nur das untergeordnete Nodes-Objekt übergeben, das aufgrund des neuen JSON-Codes, der sich aus Änderungen am Skript PHP ergibt, jetzt Tag-Namen als Schlüssel enthält, anstatt indizierte Schlüssel mit den Elementen als Kinder. Wenn wir den JSON nicht vereinfacht hätten, wäre an dieser Stelle eine zweite Schleife erforderlich gewesen, um jedes Element abzurufen.
Sie werden auch feststellen, dass die Attribute aria-
und role
enthalten sind. Auf diese Weise sind Sie bei Bedarf vollständig erreichbar.
Es bietet Ihnen auch eine praktische und semantische Möglichkeit, CSS und den Toggle-Status zu steuern. Dies wird im zusätzlichen Click-Handler am Ende des Skripts und im CSS-Beispiel am Ende dieser Antwort demonstriert.
$(document).ready(function () {
function traverse(data, firstTime) {
if (typeof data === 'object') {
var ul = '<ul role="' + (firstTime ? 'tree' : 'group' ) + '">';
$.each(data, function (key, val) {
if (key !== 'attributes' && key !== 'value') {
if (val['child_nodes']) {
ul += '<li aria-expanded="true" role="tree-item" tabindex="0">';
ul += key;
ul += traverse(val['child_nodes']);
ul += '</li>';
} else {
ul += '<li role="tree-item" tabindex="0">';
ul += key;
ul += '</li>';
}
}
});
ul += '</ul>';
return ul;
}
}
$('.btn-search').on('click', function () {
var url = $('#url').val();
if (url) {
$.get(
'getHTML.php',
{
url: url
},
function () {
$('#preview-sec iframe').attr('src', url);
$('#tree-sec').empty();
$.get(
'results.json',
function (json) {
$('#tree-sec').append(traverse(json, true));
},
'json'
);
},
'html'
);
}
});
$('#tree-sec').on('click', 'li[aria-expanded]', function (e) {
e.stopPropagation();
$(this)
.attr('aria-expanded', function (i, attr) {
return !(attr === 'true');
})
.children('ul')
.attr('aria-hidden', function (i, attr) {
return !(attr === 'true');
})
.toggle();
});
});
Schließlich bietet, wie oben erwähnt, das Vorhandensein der Attribute aria-
und role
eine semantische und bequeme Möglichkeit, die Stile zu steuern.
ul[role='tree'] {
margin-left: 1em;
padding-left: 0;
}
ul[role='tree'] li {
cursor: default;
margin: 0;
padding: 0 0 0 20px;
font: normal 1em sans-serif;
color: #333;
}
ul[role='tree'] li[aria-expanded] {
cursor: pointer;
font-weight: bold;
color: #111;
background: transparent 0 0 no-repeat url('images/arrow-Sprite.png');
}
ul[role='tree'] li[aria-expanded="true"] {
background-position: 0 0;
}
ul[role='tree'] li[aria-expanded="false"] {
background-position: 0 20px;
}
Schauen Sie sich diese Bibliothek an die von Jack geschrieben wurde.
https://github.com/Jxck/html2json
Hoffe es hilft dir.
Konstruieren Sie ein mehrdimensionales PHP -Array für Ihre HTML-Tags und geben Sie dieses Array als Eingabe für die in PHP eingebaute Funktion json_encode ($ array) ein. Dadurch wird eine baumstrukturierte json-Ausgabe zurückgegeben
Schauen Sie sich die XSLT-Verarbeitung an. funktioniert gut mit viel weniger Code-Aufwand
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="json.xml"?>
<html>
<body>
<h1>title</h1>
<h2>title 2</h2>
<h3>title 3</h3>
<ul>
<li>t1</li>
<li>t2</li>
<li>t3</li>
</ul>
</body>
</html>
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template name="json" match="/">
<xsl:for-each select="*">
{"<xsl:value-of select="local-name()"/>": "<xsl:if test="count(*)=0"><xsl:value-of select="text()"/></xsl:if>",
"attributes": {<xsl:for-each select="@*"><xsl:if test="position()>1">,</xsl:if>"<xsl:value-of select="local-name()"/>": "<xsl:value-of select="text()"/>"</xsl:for-each>},<xsl:call-template name="json"/>}</xsl:for-each>
</xsl:template>
</xsl:stylesheet>