2010-02-10 38 views
8

Sử dụng PHP Tôi đang cố gắng lấy một chuỗi HTML được chuyển từ trình soạn thảo WYSIWYG và thay thế con của phần tử bên trong tài liệu HTML được tải sẵn bằng HTML mới.PHP DOMDocument thay thế DOMElement con bằng chuỗi HTML

Cho đến nay tôi đang tải tài liệu xác định phần tử mà tôi muốn thay đổi bằng ID nhưng quá trình chuyển đổi HTML thành thứ gì đó có thể được đặt bên trong DOMElement sẽ giúp tôi tránh khỏi.

libxml_use_internal_errors(true); 

$doc = new DOMDocument(); 
$doc->loadHTML($html); 

$element = $doc->getElementById($item_id); 
if(isset($element)){ 
    //Remove the old children from the element 
    while($element->childNodes->length){ 
     $element->removeChild($element->firstChild); 
    } 

    //Need to build the new children from $html_string and append to $element 
} 

Trả lời

13

Nếu chuỗi HTML có thể được phân tích như XML, bạn có thể làm điều này (sau khi thanh toán bù trừ các yếu tố của tất cả các nút con):

$fragment = $doc->createDocumentFragment(); 
$fragment->appendXML($html_string); 
$element->appendChild($fragment); 

Nếu $ html_string không thể được phân tích như XML, nó sẽ thất bại. Nếu có, bạn sẽ phải sử dụng loadHTML(), điều này ít nghiêm ngặt hơn - nhưng nó sẽ thêm các phần tử xung quanh đoạn mà bạn sẽ phải loại bỏ.

Không giống như PHP, Javascript có thuộc tính innerHTML cho phép bạn thực hiện việc này rất dễ dàng. Tôi cần một cái gì đó giống như nó cho một dự án vì vậy tôi đã mở rộng DOMElement của PHP để bao gồm quyền truy cập innerHTML giống như Javascript.

Với nó, bạn có thể truy cập các thuộc tính innerHTML và thay đổi nó giống như bạn trong Javascript:

echo $element->innerHTML; 
$elem->innerHTML = '<a href="http://example.org">example</a>'; 

Nguồn: http://www.keyvan.net/2012/11/php-domdocument-replace-domelement-child-with-html-string/

+0

@Greg, tôi có nên quyết định nơi đóng góp của mình không? Và từ khi nào bạn nói cho thế giới? Tôi quyết định di chuyển những đóng góp của mình lên blog của chính mình sau khi một số đóng góp của tôi đã bị xóa khỏi StackOverflow và bị ẩn khỏi tôi. Tôi muốn giữ nó theo cách đó, vì vậy hãy hoàn nguyên thay đổi. – Keyvan

+0

Liên kết tới giải pháp tiềm năng luôn được chào đón, nhưng vui lòng thêm ngữ cảnh xung quanh liên kết để người dùng đồng nghiệp của bạn sẽ biết ý tưởng đó là gì và tại sao lại có nó. Luôn trích dẫn phần có liên quan nhất của một liên kết quan trọng, trong trường hợp trang web mục tiêu không thể truy cập được hoặc sẽ vĩnh viễn ngoại tuyến. Nguồn: [* Cách trả lời *] (http://stackoverflow.com/questions/how-to-answer) – Greg

+0

@Greg, tôi biết các nguyên tắc. Tôi đã đăng câu trả lời ở đây ban đầu và chuyển nó đến trang web của riêng tôi do cách đóng góp khác của tôi đã được xử lý trên trang web này - như tôi đã đề cập ở trên, chúng đã bị xóa và ẩn khỏi tôi. Tại sao bạn phản đối quá nhiều đến một liên kết nằm ngoài tôi. Một số thực phẩm được tư tưởng từ một trong những người sáng tạo của trang web này http://www.codinghorror.com/blog/2009/08/are-you-a-digital-sharecropper.html "Đóng góp của bạn có thể bị thu hồi, xóa hoặc vĩnh viễn đưa ra ngoại tuyến mà không có sự đồng ý của bạn? " Trên Stackoverflow: có. Trên trang web của riêng tôi: không. – Keyvan

1

Bạn có thể sử dụng loadHTML() trên một đoạn mã và sau đó nối thêm các nút đã tạo vào cây DOM gốc.

+0

Bạn có đang đề xuất tạo DOMDocument mới bằng cách sử dụng HTML tải sau đó lấy con của thẻ body của Tài liệu mới và thêm chúng vào DOM gốc không? Hoặc là có một hàm loadHTML() khác mà tôi đang thiếu. – AWinter

+0

Tôi thực sự ghét cách html và thẻ body được thêm tự động khi bạn làm những việc như saveHTML() hoặc loadHTML(). Có một cách giải quyết dễ dàng khác hơn là viết một wrapper mà sẽ tước chúng đi? –

0

Tôi biết đây là một chủ đề cũ (nhưng trả lời trên điều này bởi vì cũng tìm kiếm một giải pháp cho điều này). Tôi đã thực hiện một phương pháp dễ dàng để thay thế nội dung chỉ bằng một dòng duy nhất khi sử dụng nó. Để hiểu được phương thức tốt hơn, tôi cũng thêm một số ngữ cảnh có tên hàm.

Đây là một phần của thư viện của tôi, vì vậy đó là lý do của tất cả các tên hàm ở đây, tất cả các hàm bắt đầu bằng tiền tố 'su'.

Rất dễ sử dụng và rất mạnh (và ít mã hơn).

Đây là mã:

function suSetHtmlElementById(&$oDoc, &$s, $sId, $sHtml, $bAppend = false, $bInsert = false, $bAddToOuter = false) 
{ 
    if(suIsValidString($s) && suIsValidString($sId)) 
    { 
    $bCreate = true; 
    if(is_object($oDoc)) 
    { 
     if(!($oDoc instanceof DOMDocument)) 
     { return false; } 
     $bCreate = false; 
    } 

    if($bCreate) 
     { $oDoc = new DOMDocument(); } 

    libxml_use_internal_errors(true); 
    $oDoc->loadHTML($s); 
    libxml_use_internal_errors(false); 
    $oNode = $oDoc->getElementById($sId); 

    if(is_object($oNode)) 
    { 
     $bReplaceOuter = (!$bAppend && !$bInsert); 

     $sId = uniqid('SHEBI-'); 
     $aId = array("<!-- $sId -->", "<!--$sId-->"); 

     if($bReplaceOuter) 
     { 
     if(suIsValidString($sHtml)) 
     { 
      $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); 
      $s = $oDoc->saveHtml(); 
      $s = str_replace($aId, $sHtml, $oDoc->saveHtml()); 
     } 
     else { $oNode->parentNode->removeChild($oNode); 
       $s = $oDoc->saveHtml(); 
       } 
     return true; 
     } 

     $bReplaceInner = ($bAppend && $bInsert); 
     $sThis = null; 

     if(!$bReplaceInner) 
     { 
     $sThis = $oDoc->saveHTML($oNode); 
     $sThis = ($bInsert?$sHtml:'').($bAddToOuter?$sThis:(substr($sThis,strpos($sThis,'>')+1,-(strlen($oNode->nodeName)+3)))).($bAppend?$sHtml:''); 
     } 

     if(!$bReplaceInner && $bAddToOuter) 
     { 
      $oNode->parentNode->replaceChild($oDoc->createComment($sId), $oNode); 
      $sId = &$aId; 
     } 
     else { $oNode->nodeValue = $sId; } 

     $s = str_replace($sId, $bReplaceInner?$sHtml:$sThis, $oDoc->saveHtml()); 
     return true; 
    } 
    } 
    return false; 
} 

// A function of my library used in the function above: 
function suIsValidString(&$s, &$iLen = null, $minLen = null, $maxLen = null) 
{ 
    if(!is_string($s) || !isset($s{0})) 
    { return false; } 

    if($iLen !== null) 
    { $iLen = strlen($s); } 

    return (($minLen===null?true:($minLen > 0 && isset($s{$minLen-1}))) && 
      $maxLen===null?true:($maxLen >= $minLen && !isset($s{$maxLen}))); 
} 

Một số chức năng bối cảnh:

function suAppendHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false); } 

function suInsertHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true); } 

function suAddHtmlBeforeById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, true, true); } 

function suAddHtmlAfterById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, false, true); } 

function suSetHtmlById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, true, true); } 

function suReplaceHtmlElementById(&$s, $sId, $sHtml, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, $sHtml, false, false); } 

function suRemoveHtmlElementById(&$s, $sId, &$oDoc = null) 
{ return suSetHtmlElementById($oDoc, $s, $sId, null, false, false); } 

Làm thế nào để sử dụng nó:

Trong ví dụ sau đây, tôi cho rằng đã có nội dung được tải vào một variab le được gọi là $sMyHtml và biến số $sMyNewContent chứa một số html mới. Biến số $sMyHtml chứa phần tử có tên/với id 'example_id'.

// Example 1: Append new content to the innerHTML of an element (bottom of element): 
if(suAppendHtmlById($sMyHtml, 'example_id', $sMyNewContent)) 
{ echo $sMyHtml; } 
else { echo 'Element not found?'; } 

// Example 2: Insert new content to the innerHTML of an element (top of element): 
suInsertHtmlById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 3: Add new content ABOVE element: 
suAddHtmlBeforeById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 3: Add new content BELOW/NEXT TO element: 
suAddHtmlAfterById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 4: SET new innerHTML content of element: 
suSetHtmlById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 5: Replace entire element with new content: 
suReplaceHtmlElementById($sMyHtml, 'example_id', $sMyNewContent);  

// Example 6: Remove entire element: 
suSetHtmlElementById($sMyHtml, 'example_id'); 
+0

@brasofilo, bạn có thay đổi tất cả các bài đăng của mình không? Silly bạn! – Codebeat

+0

Không, tôi hy vọng rằng ** bạn sẽ làm điều đó **;) http://meta.stackexchange.com/questions/28416/what-is-the-policy-on-signatures-and-links- trong câu trả lời – brasofilo

+0

@brasofilo, xin lỗi, buồn cho bạn, tôi có một số điều quan trọng khác cần làm. – Codebeat

1

Câu trả lời được chấp nhận hiện tại cho thấy sử dụng appendXML() nhưng thừa nhận rằng nó sẽ không xử lý html phức tạp như được trả về từ trình soạn thảo WYSISYG như được chỉ định trong câu hỏi gốc. Theo đề nghị loadHTML() có thể giải quyết vấn đề này. nhưng chưa có ai thể hiện như thế nào.

Đây là những gì tôi tin là câu trả lời đúng nhất/chính xác cho câu hỏi gốc giải quyết vấn đề mã hóa, "Tài liệu Fragment trống" cảnh báo và "Lỗi tài liệu sai" lỗi mà ai đó có khả năng nhấn nếu họ viết từ đầu. Tôi biết tôi đã tìm thấy chúng sau khi làm theo các gợi ý trong các câu trả lời trước đó.

Đây là mã từ trang web tôi hỗ trợ chèn nội dung thanh bên WordPress vào nội dung $ của bài đăng. Nó giả định rằng $ doc là một DOMDocument hợp lệ tương tự như cách $ doc được định nghĩa trong câu hỏi ban đầu. Nó cũng giả định rằng $ element là thẻ mà bạn muốn chèn sidebarcontent (hoặc bất kỳ thứ gì).

  // NOTE: Cannot use a document fragment here as the AMP html is too complex for the appendXML function to accept. 
      // Instead create it as a document element and insert that way. 
      $node = new DOMDocument(); 
      // Note that we must encode it correctly or strange characters may appear. 
      $node->loadHTML(mb_convert_encoding($sidebarContent, 'HTML-ENTITIES', 'UTF-8')); 
      // Now we need to move this document element into the scope of the content document 
      // created above or the insert/append will be rejected. 
      $node = $doc->importNode($node->documentElement, true); 
      // If there is a next sibling, insert before it. 
      // If not, just add it at the end of the element we did find. 
      if ( $element->nextSibling) { 
       $element->parentNode->insertBefore($node, $element->nextSibling); 
      } else { 
       $element->parentNode->appendChild($node); 
      } 

Sau khi tất cả điều này được thực hiện, nếu bạn không muốn có nguồn gốc của một tài liệu HTML đầy đủ với thẻ nội dung và những gì không, bạn có thể tạo ra html nhiều địa phương với điều này:

// Now because we have moved the post content into a full document, we need to get rid of the 
    // extra elements that make it a document and not a fragment 
    $body = $doc->getElementsByTagName('body'); 
    $body = $body->item(0); 

    // If you need an element with a body tag, you can do this. 
    // return $doc->savehtml($body); 

    // Extract the html from the body tag piece by piece to ensure valid html syntax in destination document 
    $bodyContent = ''; 
    foreach($body->childNodes as $node) { 
      $bodyContent .= $body->ownerDocument->saveHTML($node); 
    } 
    // Now return the full content with the new content added. 
    return $bodyContent; 
+0

Cảm ơn bạn đã chia sẻ giải pháp này! Làm việc như một say mê! – Damneddani

+0

@Damneddani Lưu ý rằng savehtml ($ body) kết thúc trả về HTML WITH thẻ body. Nếu bạn đang chèn html vào một trang khác, điều đó sẽ tạo ra html không hợp lệ. Hãy thử làm một việc như sau: $ rootContent = ''; foreach ($ rootNode-> childNodes là $ node) { $ rootContent. = $ RootNode-> ownerDocument-> saveHTML ($ node); } // Không trả lại toàn bộ nội dung có nội dung thanh bên được thêm vào. trả về $ rootContent; –

Các vấn đề liên quan