Додаток 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 все ще буде SVGElements у просторі імен 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>

*: ну, є DOM рівень 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 вирішив ці проблеми років тому.


7
Ця відповідь досі актуальна! У мене просто з’явилася химерна помилка, де додані елементи відображаються в інспекторі елементів Chrome, але не відображаються. Якщо я 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 рівень 3 LS ніколи не переходив до браузерів. Однак, спочатку DOMParser, що використовується лише для Mozilla , зараз більш широко підтримується, але і jQuery має $.parseXML.
bobince

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), наприклад. створений з використанням Рафаеля або подібного у вашому прикладі "жорстко закодований" без написання сценарію.
  • Ви можете додати складні структури елементів у вигляді рядків, наприклад. $('svg').prepend('<defs><marker></marker><mask></mask></defs>');як ви робите в jQuery.
  • після того, як елементи додаються та $("#cont").html($("#cont").html());стануть видимими на екрані за допомогою їх атрибутів, можна редагувати за допомогою jQuery.

Редагувати:

Вищеописана методика працює лише з "жорстко кодованим" або маніпульованим DOM (= document.createElementNS і т.д.) SVG. Якщо для створення елементів використовується Рафаель, (за моїми тестами) зв'язок між об'єктами Рафаеля та 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 за допомогою методу $ ("body"). Append () jQuery. А коли елемент (фільтр) знаходиться в 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. Це буде дуже короткий злом.)


Я використовую моє (Домашнє) рішення для роботи зі SVG і маю ту саму Проблему, що і Рафаель, коли використовую "повторний трюк" з $ (# picture) .html ..., але моє рішення полягало в тому, щоб повторно запустити деякий кешований SVG елементи (маркування прямокутника, трансформація тощо)
півбіт

Ти чарівник. Я дивився на прийняту відповідь, і я був як людина, це буде біль. Потім я побачив це, і він робить все, що мені потрібно, за один короткий рядок. Дякую!
Композитор

1
ЦЕ МОЖЕ ПРИЧИНИТИ МЕНЮ МАРБЛІВ ... думав, що я можу магічним чином використовувати js DOM в'язів у просторі імен SVG, як можна подумати ... отримав інспектор тегів для розпізнавання вставок ... але не кубики до цих пір!
Gus Crawford

Для мене чудово працювали, додаючи мій існуючий полігон та теги до новоствореного батьківського тегу 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");

приємна відповідь. це допомогло мені з якихось питань тут.
ismail baig

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 і його можна запитати звичним способом, використовуючи напр. $ ('c'). attr ('заповнити', 'червоний').
Тімо Кахьонен

Я зробив додатковий приклад цього: jsbin.com/inevaz/1 . Він отримує вихідний код tiger.svg з Інтернету до iframe, і його можна скопіювати на текст. Вміст textarea потім використовується як джерело для вбудованого svg, яке потім доступне через DOM (для зміни стилю обведення).
Тимо Кахьонен

Це чудово, я не знаю, чому всі голосують за ті інші відповіді, які просто не працюють.
Дастін Пуассан

Це була найкраща відповідь і для мене. Мені знадобився svg з <use xlink: href = "# icon"> і це була найкоротша відповідь.
Ейдамос

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-дельфіна, але за допомогою функції помічника:

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

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, який мав "Твій Новий", і закінчую двома tspans, один із "Вашим" у ньому (чорний) та другий із "Новим у ньому" (синій ш / прохід). Jeez, знизившись з 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.