Jquery append не работает с элементом svg?

Предполагая это:

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

Почему я ничего не вижу?

Ответ 1

Когда вы передаете строку разметки в $, она анализируется как HTML, используя свойство браузера innerHTML на <div> (или другом подходящем контейнере для особых случаев, таких как <tr>). innerHTML не может анализировать SVG или другой контент, отличный от HTML, и даже если бы он не мог сказать, что <circle> должен был находиться в пространстве имен SVG.

innerHTML недоступен в SVGElement - это свойство только HTMLElement. В настоящее время нет свойства innerSVG или другого способа (*) для анализа содержимого в SVGElement. По этой причине вам следует использовать методы типа DOM. jQuery не дает вам легкий доступ к методам, поддерживающим имена, необходимые для создания элементов SVG. На самом деле jQuery не предназначен для использования с SVG вообще, и многие операции могут завершиться неудачно.

HTML5 promises, чтобы вы могли использовать <svg> без xmlns внутри обычного документа HTML (text/html) в будущем. Но это всего лишь взлом для анализатора (**), содержимое SVG по-прежнему будет SVGElements в пространстве имен SVG, а не HTMLElements, поэтому вы не сможете использовать innerHTML, хотя они выглядят как часть HTML-документа.

Однако на сегодняшний день для браузеров вы должны использовать XHTML (правильно служил application/xhtml+xml; сохранить с расширением .xhtml для локального тестирования), чтобы заставить SVG работать вообще. (В любом случае это имеет смысл: SVG - это стандарт, основанный на XML). Это означает, что вам нужно будет избежать символов < внутри вашего блока script (или заключить в раздел CDATA) и включить XHTML xmlns. Пример:

<!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>

*: ну, там DOM Level 3 LS parseWithContext, но поддержка браузера очень плохая. Изменить для добавления: однако, пока вы не можете вводить разметку в SVGElement, вы можете добавить новый SVGElement в HTMLElement с помощью innerHTML, а затем перенести его на желаемую цель. Скорее всего, это будет немного медленнее:

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

**: Я ненавижу то, как авторы HTML5, похоже, боятся XML и настроены на использование функций, основанных на XML, в беспорядочном беспорядке, который является HTML. XHTML решил эти проблемы много лет назад.

Ответ 2

принятый ответ показывает слишком сложный способ. Как утверждает Форресто в его ответе, "похоже, что он добавляет их в DOM-проводник, но не на экране", и причиной этого являются разные пространства имен для html и SVG.

Простейшим обходным решением является "обновление" всего svg. После добавления круга (или других элементов) используйте это:

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

Это трюк. Круг находится на экране.

Или, если хотите, используйте контейнер div:

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

И заверните свой svg внутри контейнера div:

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

Функциональный пример:
http://jsbin.com/ejifab/1/edit

Преимущества этой техники:

  • вы можете редактировать существующие svg (это уже в DOM), например. созданный с использованием Raphael или как в вашем примере "жестко закодированный" без скриптов.
  • вы можете добавлять сложные структуры элементов как строки, например. $('svg').prepend('<defs><marker></marker><mask></mask></defs>'); как вы делаете в jQuery.
  • после добавления и отображения элементов на экране с помощью $("#cont").html($("#cont").html()); их атрибуты можно редактировать с помощью jQuery.

EDIT:

Вышеупомянутая техника работает с "жестко закодированным" или DOM, управляемым (= document.createElementNS и т.д.) только SVG. Если Raphael используется для создания элементов (согласно моим тестам), связь между объектами Рафаэля и SVG DOM нарушается, если используется $("#cont").html($("#cont").html());. Обходной путь к этому - не использовать $("#cont").html($("#cont").html()); вообще, а вместо него использовать фиктивный документ SVG.

Этот фиктивный SVG является первым текстовым представлением документа SVG и содержит только те элементы, которые необходимы. Если мы хотим, например. чтобы добавить элемент фильтра в документ Рафаэля, манекен может быть чем-то вроде <svg id="dummy" style="display:none"><defs><filter><!-- Filter definitons --></filter></defs></svg>. Текстовое представление сначала преобразуется в DOM с использованием метода jQuery $( "body" ). Append(). И когда элемент (фильтр) находится в DOM, он может быть запрошен с использованием стандартных методов jQuery и добавлен к основному документу SVG, который создается Рафаэлем.

Почему этот манекен нужен? Почему бы не добавить элемент фильтра строго в созданный документ Рафаэля? Если вы попытаетесь использовать, например. $("svg").append("<circle ... />"), он создается как элемент html, и ничего не отображается на экране, как описано в ответах. Но если весь документ SVG добавляется, браузер автоматически обрабатывает преобразование пространства имен всех элементов в документе SVG.

Пример иллюстрирует технику:

// 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)");

Полная рабочая демонстрация этого метода находится здесь: http://jsbin.com/ilinan/1/edit.

(У меня (пока) нет идеи, почему $("#cont").html($("#cont").html()); не работает при использовании Raphael. Это будет очень короткий взлом.)

Ответ 3

Все более популярная библиотека D3 обрабатывает странности добавления/управления svg очень красиво. Вы можете захотеть использовать его в отличие от jQuery hacks, упомянутых здесь.

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

Ответ 4

JQuery не может добавлять элементы к <svg> (похоже, они добавляются в DOM-проводник, но не на экран).

Один способ - добавить <svg> со всеми элементами, которые вам нужны на странице, а затем изменить атрибуты элементов с помощью .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/

Ответ 5

Я не видел, чтобы кто-то упоминал этот метод, но document.createElementNS() полезен в этом случае.

Вы можете создавать элементы, используя ванильный Javascript как обычные DOM-узлы с правильным пространством имен, а затем jQuery-ify их оттуда. Например:

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

Единственная нижняя сторона состоит в том, что вам нужно создать каждый элемент SVG с правом пространством имен отдельно или не работать.

Ответ 6

Я вижу круг в firefox, делая 2 вещи:

1) Переименование файла из html в xhtml

2) Измените script на

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

Ответ 7

На основании ответа @chris-dolphin, но используя вспомогательную функцию:

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

Ответ 8

Если строка, которую вам нужно добавить, это SVG, и вы добавляете правильное пространство имен, вы можете проанализировать строку как XML и добавить к родительскому.

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

Ответ 9

Принятый ответ Бобинса - это короткое портативное решение. Если вам нужно не только добавлять SVG, но и манипулировать им, вы можете попробовать библиотеку JavaScript "Pablo" (я ее написал). Он будет хорошо знаком с пользователями jQuery.

Пример вашего кода выглядел бы так:

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

Вы также можете создавать элементы SVG "на лету" без указания разметки:

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

Ответ 10

Нашел простой способ, который работает со всеми браузерами, которые у меня есть (Chrome 49, Edge 25, Firefox 44, IE11, Safari 5 [Win], Safari 8 (MacOS)):

// Clean svg content (if you want to update the svg 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"/>');

// refresh DOM (you must refresh svg 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>

Ответ 11

Более простой способ - просто сгенерировать SVG в строку, создать HTML-элемент оболочки и вставить строку svg в элемент HTML с помощью $( "# wrapperElement" ). html (svgString). Это прекрасно работает в Chrome и Firefox.