Додаток 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.