phần bổ sung của jquery không hoạt động với phần tử svg?


199

Giả sử điều này:

<html>
<head>
 <script type="text/javascript" src="jquery.js"></script>
 <script type="text/javascript">
 $(document).ready(function(){
  $("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
 });
 </script>
</head>
<body>
 <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px">
 </svg>
</body>

Tại sao tôi không thấy gì?

jquery  html  svg 

Câu trả lời:


249

Khi bạn chuyển một chuỗi đánh dấu vào $, nó được phân tích cú pháp dưới dạng HTML bằng cách sử dụng thuộc tính của trình duyệt innerHTMLtrên một <div>(hoặc thùng chứa phù hợp khác cho các trường hợp đặc biệt như <tr>). innerHTMLkhông thể phân tích SVG hoặc nội dung không phải HTML khác và ngay cả khi nó không thể nói rằng nó <circle>được cho là nằm trong không gian tên SVG.

innerHTMLkhông có sẵn trên SVGEuity, nó chỉ là một thuộc tính của HTMLEuity. Hiện tại không có innerSVGtài sản hoặc cách nào khác (*) để phân tích nội dung thành một SVGEuity. Vì lý do này, bạn nên sử dụng các phương thức kiểu DOM. jQuery không cung cấp cho bạn quyền truy cập dễ dàng vào các phương thức được đặt tên cần thiết để tạo các phần tử SVG. Thực sự jQuery không được thiết kế để sử dụng với SVG và nhiều hoạt động có thể thất bại.

HTML5 hứa hẹn sẽ cho phép bạn sử dụng <svg>mà không cần có tài liệu xmlnsHTML ( text/html) đơn giản trong tương lai. Nhưng đây chỉ là một trình phân tích cú pháp (**), nội dung SVG vẫn sẽ là SVGElements trong không gian tên SVG chứ không phải HTMLElements, vì vậy bạn sẽ không thể sử dụng innerHTMLmặc dù chúng trông giống như một phần của tài liệu HTML.

Tuy nhiên, đối với các trình duyệt ngày nay, bạn phải sử dụng X HTML (được phân phát đúng như application/xhtml+xml; lưu với phần mở rộng tệp .xhtml để kiểm tra cục bộ) để SVG hoạt động hoàn toàn. (Dù sao thì cũng hợp lý; SVG là một tiêu chuẩn dựa trên XML đúng.) Điều này có nghĩa là bạn phải thoát các <ký hiệu bên trong khối tập lệnh của mình (hoặc gửi kèm trong phần CDATA) và bao gồm xmlnskhai báo XHTML . thí dụ:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><head>
</head><body>
    <svg id="s" xmlns="http://www.w3.org/2000/svg"/>
    <script type="text/javascript">
        function makeSVG(tag, attrs) {
            var el= document.createElementNS('http://www.w3.org/2000/svg', tag);
            for (var k in attrs)
                el.setAttribute(k, attrs[k]);
            return el;
        }

        var circle= makeSVG('circle', {cx: 100, cy: 50, r:40, stroke: 'black', 'stroke-width': 2, fill: 'red'});
        document.getElementById('s').appendChild(circle);
        circle.onmousedown= function() {
            alert('hello');
        };
    </script>
</body></html>

*: tốt, có parseWithContext của DOM Cấp 3 LS , nhưng hỗ trợ trình duyệt rất kém. Chỉnh sửa để thêm: tuy nhiên, trong khi bạn không thể đưa đánh dấu vào SVGEuity, bạn có thể đưa SVGE bổ sung mới vào HTMLEuity bằng cách sử dụng innerHTML, sau đó chuyển nó vào mục tiêu mong muốn. Nó có thể sẽ chậm hơn một chút:

<script type="text/javascript"><![CDATA[
    function parseSVG(s) {
        var div= document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
        div.innerHTML= '<svg xmlns="http://www.w3.org/2000/svg">'+s+'</svg>';
        var frag= document.createDocumentFragment();
        while (div.firstChild.firstChild)
            frag.appendChild(div.firstChild.firstChild);
        return frag;
    }

    document.getElementById('s').appendChild(parseSVG(
        '<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" onmousedown="alert(\'hello\');"/>'
    ));
]]></script>

**: Tôi ghét cách các tác giả của HTML5 dường như sợ XML và quyết tâm chuyển các tính năng dựa trên XML vào mớ hỗn độn đó là HTML. XHTML đã giải quyết những vấn đề này nhiều năm trước.


7
Câu trả lời này vẫn có liên quan! Tôi vừa gặp một lỗi kỳ lạ khi các yếu tố được thêm vào hiển thị trong trình kiểm tra phần tử Chrome, nhưng sẽ không hiển thị. Nếu tôi RMB> edit as htmltrên thẻ html và nhấn enter, mọi thứ sẽ hiển thị (nhưng tất cả các trình lắng nghe sự kiện đều biến mất). Sau khi đọc câu trả lời này, tôi đã thay đổi các lệnh gọi createdEuity của mình thành createdEuityNS và bây giờ mọi thứ đều hoạt động!
Kitsu.eb

7
Bạn có thể thao tác các phần tử SVG bằng jquery sau khi chúng được tạo bằng phương thức DOM createdEuityNS. Bạn có thể thay đổi hàm makeSVG thành 'return $ (el)' và bây giờ bạn có một phần tử svg có thể sử dụng các phương thức jquery.
Hoffmann

6
Ah! Vì vậy, đó là lý do tại sao nó sẽ không hoạt động. Tôi cố gắng điều chính xác cùng với hai thư viện khác nhau, một trong jQuery và một ở D3.js . Tôi đã nhận được chính xác cùng một đầu ra nguồn trong HTML bằng cả hai, nhưng các phần tử do jQuery tạo sẽ không hiển thị trong khi các phần tử do D3 tạo ra đã làm! Tôi khuyên bạn nên sử dụng D3:d3.select('body').append('svg').attr('width','100%');
chharvey

3
@MadsSkjern: DOM Cấp 3 LS không bao giờ được đưa lên trình duyệt. Mặc dù vậy, DOMParser chỉ có Mozilla ban đầu được hỗ trợ rộng rãi hơn và jQuery có $.parseXML.
bobince

1
Nó vẫn có liên quan. bobince ghét sự lộn xộn của HTML5, tôi ghét sự lộn xộn của toàn bộ web, bởi vì không nơi nào bạn có thể tìm thấy mã tốt mà không bị hack vì sự lộn xộn đó. WWW nên được đổi tên thành WWM World Wide Mess.
Olivier Pons

149

Các câu trả lời được chấp nhận chương trình quá phức tạp cách. Như Forresto tuyên bố trong câu trả lời của mình , " dường như có thêm chúng trong trình thám hiểm DOM, nhưng không phải trên màn hình " và lý do cho điều này là các không gian tên khác nhau cho html và svg.

Cách giải quyết đơn giản nhất là "làm mới" toàn bộ svg. Sau khi nối thêm vòng tròn (hoặc các yếu tố khác), hãy sử dụng:

$("body").html($("body").html());

Đây là mẹo. Vòng tròn nằm trên màn hình.

Hoặc nếu bạn muốn, hãy sử dụng div container:

$("#cont").html($("#cont").html());

Và bọc Svg của bạn bên trong div container:

<div id="cont">
    <svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100" width="200px" height="100px">
    </svg>
</div>

Ví dụ về chức năng:
http://jsbin.com/ejifab/1/edit

Ưu điểm của kỹ thuật này:

  • bạn có thể chỉnh sửa svg hiện có (đã có trong DOM), vd. được tạo bằng Raphael hoặc giống như trong ví dụ của bạn "mã hóa cứng" mà không cần kịch bản.
  • bạn có thể thêm các cấu trúc phần tử phức tạp như các chuỗi, ví dụ. $('svg').prepend('<defs><marker></marker><mask></mask></defs>');giống như bạn làm trong jQuery.
  • sau khi các phần tử được nối thêm và hiển thị trên màn hình bằng $("#cont").html($("#cont").html());các thuộc tính của chúng có thể được chỉnh sửa bằng jQuery.

BIÊN TẬP:

Kỹ thuật trên chỉ hoạt động với "mã hóa cứng" hoặc DOM được thao tác (= document.createEuityNS, v.v.) chỉ SVG. Nếu Raphael được sử dụng để tạo các phần tử, (theo thử nghiệm của tôi), liên kết giữa các đối tượng Raphael và SVG DOM bị phá vỡ nếu $("#cont").html($("#cont").html());được sử dụng. Cách giải quyết cho vấn đề này là hoàn toàn không sử dụng $("#cont").html($("#cont").html());và thay vào đó nó sử dụng tài liệu SVG giả.

SVG giả này trước tiên là một đại diện văn bản của tài liệu SVG và chỉ chứa các yếu tố cần thiết. Nếu chúng ta muốn ví dụ. để thêm một phần tử bộ lọc vào tài liệu Raphael, hình nộm có thể giống như thế <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. Biểu diễn văn bản trước tiên được chuyển đổi sang DOM bằng phương thức $ ("body"). Append () của jQuery. Và khi phần tử (bộ lọc) có trong DOM, nó có thể được truy vấn bằng các phương thức jQuery tiêu chuẩn và được thêm vào tài liệu SVG chính được tạo bởi Raphael.

Tại sao hình nộm này là cần thiết? Tại sao không thêm một phần tử bộ lọc vào tài liệu đã tạo của Raphael? Nếu bạn thử nó bằng cách sử dụng ví dụ. $("svg").append("<circle ... />"), nó được tạo dưới dạng phần tử html và không có gì trên màn hình như được mô tả trong câu trả lời. Nhưng nếu toàn bộ tài liệu SVG được nối thêm, thì trình duyệt sẽ tự động xử lý chuyển đổi không gian tên của tất cả các thành phần trong tài liệu SVG.

Một ví dụ khai sáng kỹ thuật:

// Add Raphael SVG document to container element
var p = Raphael("cont", 200, 200);
// Add id for easy access
$(p.canvas).attr("id","p");
// Textual representation of element(s) to be added
var f = '<filter id="myfilter"><!-- filter definitions --></filter>';

// Create dummy svg with filter definition 
$("body").append('<svg id="dummy" style="display:none"><defs>' + f + '</defs></svg>');
// Append filter definition to Raphael created svg
$("#p defs").append($("#dummy filter"));
// Remove dummy
$("#dummy").remove();

// Now we can create Raphael objects and add filters to them:
var r = p.rect(10,10,100,100);
$(r.node).attr("filter","url(#myfilter)");

Bản demo hoạt động đầy đủ của kỹ thuật này có tại đây: http://jsbin.com/ilinan/1/edit .

(Tôi vẫn chưa biết, tại sao $("#cont").html($("#cont").html());không hoạt động khi sử dụng Raphael. Nó sẽ rất ngắn.)


Tôi đang sử dụng giải pháp (Làm tại nhà) của mình để xử lý SVG và gặp vấn đề tương tự như Raphael, khi sử dụng "thủ thuật sửa lỗi" với $ (# hình ảnh) .html ..., nhưng giải pháp của tôi là khởi tạo lại một số SVG được lưu trong bộ nhớ cache các phần tử (đánh dấu hình chữ nhật, biến đổi, v.v.)
Halfbit

Bạn là một phù thủy. Tôi đã nhìn vào câu trả lời được chấp nhận và tôi giống như người đàn ông này sẽ là một nỗi đau. Sau đó, tôi đã thấy điều này và nó làm mọi thứ tôi cần trong một dòng ngắn. Cảm ơn bạn!
Nhà soạn nhạc

1
ĐIỀU NÀY GÂY RA TÔI ĐỂ MANG MARBLES CỦA TÔI ... nghĩ rằng tôi có thể sử dụng phép thuật js DOM trên không gian tên SVG như mọi người có thể nghĩ ... đã có thanh tra thẻ để nhận ra các phần chèn vào ... nhưng không phải là xúc xắc cho đến bây giờ!
Gus Crawford

Làm việc rất tốt cho tôi khi gắn các thẻ đa giác và đường dẫn hiện có của tôi vào thẻ svg cha mẹ mới được tạo. Cảm ơn!
Sói Kivak

1
$("#cont").html($("#cont").html());hoạt động rất tốt trong Chrome, nhưng đối với tôi thì không hoạt động trong IE 11.
CoderDennis

37

Thư viện D3 ngày càng phổ biến xử lý các điểm kỳ lạ của việc thêm / thao tác svg rất độc đáo. Bạn có thể muốn xem xét việc sử dụng nó trái ngược với các bản hack jQuery được đề cập ở đây.

HTML

<svg xmlns="http://www.w3.org/2000/svg"></svg>

Javascript

var circle = d3.select("svg").append("circle")
    .attr("r", "10")
    .attr("style", "fill:white;stroke:black;stroke-width:5");

câu trả lời tốt đẹp nó đã giúp tôi cho một số vấn đề ở đây.
ismail baig

jQuery cũng bóp nghẹt việc thiết lập / nhận các lớp từ SVG. Chỉ là một bên.
Queue Hammer

24

JQuery không thể nối các phần tử vào <svg>(dường như thêm chúng vào trình thám hiểm DOM, nhưng không phải trên màn hình).

Một cách giải quyết khác là nối thêm một <svg>với tất cả các yếu tố mà bạn cần vào trang, sau đó sửa đổi các thuộc tính của các thành phần bằng cách sử dụng .attr().

$('body')
  .append($('<svg><circle id="c" cx="10" cy="10" r="10" fill="green" /></svg>'))
  .mousemove( function (e) {
      $("#c").attr({
          cx: e.pageX,
          cy: e.pageY
      });
  });

http://jsfiddle.net/8FBjb/1/


Cảm ơn, đã giúp tôi rất nhiều! Đây là một cách tiếp cận rất tốt. Thay vì nối thêm <circle>hoặc các phần tử khác bằng cách sử dụng các phương thức DOM phức tạp và chậm (ví dụ: createDocumentFragment()hoặc createElementNS()), hãy nối toàn bộ tài liệu svg vào trong thùng chứa. Thay vì $ ('body'), tất nhiên cũng có thể là $ ('div'). Và sau đó, tài liệu svg có trên DOM và có thể được truy vấn theo cách quen thuộc bằng cách sử dụng, vd. $ ('c'). attr ('điền', 'đỏ').
Timo Kähkönen

Tôi đã làm một ví dụ bổ sung về điều này: jsbin.com/inevaz/1 . Nó nhận được mã nguồn tiger.svg từ internet đến iframe và nó có thể được Sao chép-dán vào textarea. Nội dung của textarea sau đó được sử dụng làm nguồn cho svg nội tuyến, sau đó có thể truy cập thông qua DOM (để thay đổi kiểu đột quỵ).
Timo Kähkönen

Điều này thật tuyệt, tôi không biết tại sao mọi người lại bỏ phiếu cho những câu trả lời khác mà đơn giản là không hiệu quả.
Dustin Poissant

Đây là câu trả lời tốt nhất cho tôi quá. Tôi cần một svg với <use xlink: href = "# icon"> và đây là câu trả lời ngắn nhất.
Eydamos

17

Tôi chưa thấy ai đó đề cập đến phương pháp này nhưng document.createElementNS()rất hữu ích trong trường hợp này.

Bạn có thể tạo các phần tử bằng cách sử dụng vanilla Javascript như các nút DOM bình thường với không gian tên chính xác và sau đó jQuery-ify chúng từ đó. Thích như vậy:

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
    circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');

var $circle = $(circle).attr({ //All your attributes });

$(svg).append($circle);

Mặt trái duy nhất là bạn phải tạo từng phần tử SVG với không gian tên đúng nếu không nó sẽ không hoạt động.


2
Câu trả lời của Timo (được bình chọn hàng đầu) đã hoạt động trong Chrome, nhưng không phải IE. Câu trả lời này đã giải quyết vấn đề cho cả hai trình duyệt!
CoderDennis

1
Đẹp! Những điều cần biết
Chris Dolphin

14

Tìm thấy một cách dễ dàng hoạt động với tất cả các trình duyệt tôi có (Chrome 49, Edge 25, Firefox 44, IE11, Safari 5 [Win], Safari 8 (MacOS)):

// Clean svg content (if you want to update the svg's objects)
// Note : .html('') doesn't works for svg in some browsers
$('#svgObject').empty();
// add some objects
$('#svgObject').append('<polygon class="svgStyle" points="10,10 50,10 50,50 10,50 10,10" />');
$('#svgObject').append('<circle class="svgStyle" cx="100" cy="30" r="25"/>');

// Magic happens here: refresh DOM (you must refresh svg's parent for Edge/IE and Safari)
$('#svgContainer').html($('#svgContainer').html());
.svgStyle
{
  fill:cornflowerblue;
  fill-opacity:0.2;
  stroke-width:2;
  stroke-dasharray:5,5;
  stroke:black;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="svgContainer">
  <svg id="svgObject" height="100" width="200"></svg>
</div>

<span>It works if two shapes (one square and one circle) are displayed above.</span>


5
Dòng làm mới đó chính xác là những gì tôi đang tìm kiếm. Trình duyệt ngớ ngẩn. Cảm ơn!
Sam Soffes

8

Tôi có thể thấy vòng tròn trong firefox, làm 2 việc:

1) Đổi tên tập tin từ html thành xhtml

2) Thay đổi tập lệnh thành

<script type="text/javascript">
$(document).ready(function(){
    var obj = document.createElementNS("http://www.w3.org/2000/svg", "circle");
    obj.setAttributeNS(null, "cx", 100);
    obj.setAttributeNS(null, "cy", 50);
    obj.setAttributeNS(null, "r",  40);
    obj.setAttributeNS(null, "stroke", "black");
    obj.setAttributeNS(null, "stroke-width", 2);
    obj.setAttributeNS(null, "fill", "red");
    $("svg")[0].appendChild(obj);
});
</script>

1
Tám năm sau và điều này vẫn hữu ích!
Don 01001100

5

Dựa trên câu trả lời của @ chris-dolphin nhưng sử dụng chức năng trợ giúp:

// Creates svg element, returned as jQuery object
function $s(elem) {
  return $(document.createElementNS('http://www.w3.org/2000/svg', elem));
}

var $svg = $s("svg");
var $circle = $s("circle").attr({...});
$svg.append($circle);

1
Tất nhiên bạn cũng có thể làm:$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
Jonas Berlin

4

Nếu chuỗi bạn cần nối là SVG và bạn thêm không gian tên thích hợp, bạn có thể phân tích chuỗi dưới dạng XML và nối thêm vào cha.

var xml = jQuery.parseXML('<circle xmlns="http://www.w3.org/2000/svg" cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
$("svg").append(xml.documentElement))

3

Câu trả lời được chấp nhận bởi Bobince là một giải pháp ngắn gọn, di động. Nếu bạn không chỉ nối thêm SVG mà còn thao tác với nó, bạn có thể thử thư viện JavaScript "Pablo" (tôi đã viết nó). Nó sẽ cảm thấy quen thuộc với người dùng jQuery.

Ví dụ mã của bạn sẽ trông như sau:

$(document).ready(function(){
    Pablo("svg").append('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>');
});

Bạn cũng có thể tạo các phần tử SVG một cách nhanh chóng mà không cần chỉ định đánh dấu:

var circle = Pablo.circle({
    cx:100,
    cy:50,
    r:40
}).appendTo('svg');

1

Tôi sẽ đề nghị có thể tốt hơn khi sử dụng ajax và tải phần tử svg từ một trang khác.

$('.container').load(href + ' .svg_element');

Trong đó href là vị trí của trang với svg. Bằng cách này, bạn có thể tránh mọi hiệu ứng lộn xộn có thể xảy ra khi thay thế nội dung html. Ngoài ra, đừng quên mở khóa Svg sau khi tải:

$('.svg_element').unwrap();

0

Điều này làm việc cho tôi ngày hôm nay với FF 57:

function () {
    // JQuery, today, doesn't play well with adding SVG elements - tricks required
    $(selector_to_node_in_svg_doc).parent().prepend($(this).clone().text("Your"));
    $(selector_to_node_in_svg_doc).text("New").attr("x", "340").text("New")
        .attr('stroke', 'blue').attr("style", "text-decoration: line-through");
}

Làm cho:

hình ảnh SVG này như đã thấy trong Firefox 57


Đó không phải là yếu tố SVG mà bạn đang nối thêm, đó là nội dung văn bản.
Robert Longson

Điều đó có thể, nhưng nó được hiển thị sau khi tải trang ban đầu của SVG được in tĩnh.
paul_h

Vì vậy, những gì, đó không phải là câu hỏi được hỏi, đó là về các yếu tố svg và không phải nội dung văn bản.
Robert Longson

$(this).clone()đang nhân bản một yếu tố SVG (và đó là con nếu có). Nhìn qua việc sử dụng text(). Tôi đang sử dụng một tspan duy nhất có "Mới của bạn" trong đó và kết thúc với hai muỗng cà phê, một có "Của bạn" trong đó (màu đen) và một có "Mới trong đó" (màu xanh lam xuyên suốt). Jeez, giảm từ 0 phiếu xuống -1 :-(
paul_h

0
 var svg; // if you have variable declared and not assigned value.
 // then you make a mistake by appending elements to that before creating element    
 svg.appendChild(document.createElement("g"));
 // at some point you assign to svg
 svg = document.createElementNS('http://www.w3.org/2000/svg', "svg")
 // then you put it in DOM
 document.getElementById("myDiv").appendChild(svg)
 // it wont render unless you manually change myDiv DOM with DevTools

// to fix assign before you append
var svg = createElement("svg", [
    ["version", "1.2"],
    ["xmlns:xlink", "http://www.w3.org/1999/xlink"],
    ["aria-labelledby", "title"],
    ["role", "img"],
    ["class", "graph"]
]);
function createElement(tag, attributeArr) {
      // .createElementNS  NS is must! Does not draw without
      let elem = document.createElementNS('http://www.w3.org/2000/svg', tag);             
      attributeArr.forEach(element => elem.setAttribute(element[0], element[1]));
      return elem;
}
// extra: <circle> for example requires attributes to render. Check if missing.

-1

Một cách đơn giản hơn nhiều là chỉ cần tạo SVG của bạn thành một chuỗi, tạo một phần tử HTML bao bọc và chèn chuỗi svg vào phần tử HTML bằng cách sử dụng $("#wrapperElement").html(svgString). Điều này chỉ hoạt động tốt trong Chrome và Firefox.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.