Приложение jquery не работает с элементом SVG?


199

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

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

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

jquery  html  svg 

Ответы:


249

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

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

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

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

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

*: хорошо, есть parseWithContext DOM Level 3 LS , но поддержка браузера очень плохая. Отредактируйте, чтобы добавить: однако, хотя вы не можете внедрить разметку в 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 решил эти проблемы много лет назад.


7
Этот ответ по-прежнему актуален! У меня была странная ошибка, когда добавленные элементы показывались в инспекторе элементов Chrome, но не отображались. Если I RMB> edit as htmlв теге html и нажать клавишу ввода, все отображается (но все прослушиватели событий исчезают). После прочтения этого ответа я изменил свои вызовы createElement на createElementNS, и теперь все работает!
— kitsu.eb

7
Вы можете манипулировать элементами SVG с помощью jquery после их создания, используя метод DOM createElementNS. Вы можете изменить функцию makeSVG на 'return $ (el)', и теперь у вас есть элемент svg, который может использовать методы jquery.
— Хоффманн

6
Ах! Вот почему это не сработает. Я попробовал ту же самую вещь с двумя различными библиотеками, один в JQuery и один в D3.js . Я получил точно такой же исходный вывод в HTML, используя оба, но сгенерированные jQuery элементы не рендерится, а сгенерированные D3! Я рекомендую использовать D3:d3.select('body').append('svg').attr('width','100%');
— chharvey

3
@MadsSkjern: DOM Level 3 LS никогда не заходил в браузеры. Первоначально DOMParser, предназначенный только для Mozilla , теперь более широко поддерживается, хотя и имеет jQuery $.parseXML.
— бобинце

1
Это все еще актуально. bobince ненавидит беспорядок HTML5, я ненавижу беспорядок всей сети, потому что нигде вы не можете найти хороший код без взломов из-за этого беспорядка. WWW следует переименовать в WWM World Wide Mess.
— Оливье Понс

149

В ответ принятые шоу слишком сложным способом. Как утверждает Форресто в своем ответе , « он, кажется, добавляет их в проводнике 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.

РЕДАКТИРОВАТЬ:

Вышеприведенный метод работает только с SVG «жестко запрограммированным» или DOM-манипулированием (= document.createElementNS и т. Д.). Если для создания элементов используется Raphael, то (согласно моим тестам) связь между объектами 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 (). А когда элемент (filter) находится в DOM, его можно запросить с помощью стандартных методов jQuery и добавить к основному документу SVG, который создан Raphael.

Зачем этот манекен нужен? Почему бы не добавить элемент фильтра строго в документ, созданный Рафаэлем? Если вы попробуете, используя, например. $("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());не работает при использовании Рафаэля. Это был бы очень короткий взлом.)


Я использую свое решение (Home made) для работы с SVG, и у меня та же проблема, что и у Raphael, когда я использую «трюк с повторным анализом» с $ (# picture) .html ..., но я решил повторно запустить некоторые кэшированные SVG элементы (маркировка прямоугольника, преобразование и т. д.)
— полубит

Вы волшебник. Я смотрел на принятый ответ, и я был как человек, это будет боль. Затем я увидел это, и он делает все, что мне нужно, в одну короткую очередь. Спасибо!
— Композитор

1
Это заставило меня потерять шрамы ... я подумал, что могу волшебным образом использовать вязы JOM DOM в пространстве имен SVG, как можно подумать ... получил инспектор тегов для распознавания вставок ... но до сих пор не выпал кубик!
— Гас Кроуфорд,

Работал отлично для меня, когда добавлял существующие теги полигона и пути к недавно созданному родительскому тегу svg. Спасибо!
— Кивак Вольф

1
$("#cont").html($("#cont").html());отлично работал в Chrome, но не работал в IE 11 для меня.
— CoderDennis

37

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

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

хороший ответ. это помогло мне в некоторой проблеме здесь.
— Исмаил Байг

JQuery также задыхается от установки / получения классов из SVG. Просто в сторону.
— QueueHammer

24

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/


Спасибо, мне очень помогло! Это очень хороший подход. Вместо добавления <circle>или других элементов, индивидуально использующих сложные и медленные DOM-методы (например, createDocumentFragment()или createElementNS()), добавьте весь документ SVG в контейнер. Вместо $ ('body') это может быть также $ ('div'). И после этого документ svg находится в DOM и может быть запрошен обычным способом с помощью, например. $ ( 'с'). атр ( 'заливка', 'красный').
— Тимо Кяхконен

Я сделал дополнительный пример этого: jsbin.com/inevaz/1 . Он получает исходный код tiger.svg из Интернета в iframe и может быть скопирован в текстовую область. Содержимое textarea затем используется в качестве источника для встроенного SVG, который затем доступен через DOM (для изменения стиля обводки).
— Тимо Кяхконен

Это здорово, я не знаю, почему все голосуют за те ответы, которые просто не работают.
— Дастин Пуассан

Это был лучший ответ и для меня. Мне нужен был svg с <use xlink: href = "# icon">, и это был самый короткий рабочий ответ.
— Eydamos

17

Я не видел, чтобы кто-то упоминал этот метод, но 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 с правильным пространством имен индивидуально, иначе он не будет работать.


2
Тимо ответ (с наибольшим количеством голосов) работал в Chrome, но не в IE. Этот ответ решил проблему для обоих браузеров!
— CoderDennis

1
Ницца! Полезно знать
— Крис Дельфин

14

Нашел простой способ, который работает со всеми браузерами (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
Эта строка обновления - именно то, что я искал. Глупые браузеры. Спасибо!
— Сэм Соффс

8

Я могу видеть круг в Firefox, делая 2 вещи:

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

2) Изменить скрипт на

<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
Восемь лет спустя, и это все еще полезно!
— Дон 01001100

5

Основано на ответе @ 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);

1
Конечно, вы также можете сделать:$svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>'));
— Йонас Берлин

4

Если вам нужно добавить строку 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))

3

Принятый ответ Bobince - это короткое, портативное решение. Если вам нужно не только добавить 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');

1

Я хотел бы предположить, что может быть лучше использовать ajax и загрузить элемент svg с другой страницы.

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

Где href - это местоположение страницы с svg. Таким образом, вы можете избежать любых нервных эффектов, которые могут возникнуть при замене HTML-контента. Также не забудьте развернуть svg после загрузки:

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

0

Это работает для меня сегодня с 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");
}

ПРОИЗВОДИТ:

это изображение SVG, как видно в Firefox 57


Это не элементы SVG, которые вы добавляете, это текстовое содержимое.
— Роберт Лонгсон

Это может быть, но он отображается после начальной загрузки страницы статически встроенного SVG.
— paul_h

Так что, это не вопрос, который задают, а об элементах svg, а не о текстовом содержимом.
— Роберт Лонгсон

$(this).clone()клонирует элемент SVG (и это дети, если он есть). Смотри мимо использования text(). Я беру один tspan, в котором было "Your New", и в итоге получился два tspan, один с "Your" в нем (черный) и один с "New in it" (синий с проходом через строку). Боже, уменьшено с 0 голосов до -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

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.