jQuery .append не является элементом, который нужно нарисовать [duplicate]

Уровень 4 выбора вводит :has() (ранее индикатор объекта !), который позволит вам выбрать предыдущего брата с:

previous:has(+ next) {}

... но в время написания, это некоторое расстояние, выходящее за пределы кровопускания для поддержки браузера.

176
задан 4 September 2010 в 12:24
поделиться

14 ответов

Когда вы передаете строку разметки в $, она анализируется как 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-документа.

Однако для современных браузеров вы должны использовать HTML-код X (должным образом исполненный как application/xhtml+xml; сохранить с расширением .xhtml для локального тестирования ), чтобы заставить SVG работать вообще. (В любом случае это имеет смысл: SVG - это стандарт, основанный на стандартах XML). Это означает, что вам придется избегать символов < внутри вашего блока сценариев (или заключить в раздел 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 решил эти проблемы много лет назад.

221
ответ дан bobince 19 August 2018 в 06:07
поделиться
  • 1
    Этот ответ по-прежнему актуальен! У меня просто была странная ошибка, когда добавленные элементы отображались в инспекторе элементов Chrome, но не отображались. Если I RMB & gt; edit as html в теге html и нажмите Enter, то отобразится все (но все прослушиватели событий исчезнут). Прочитав этот ответ, я изменил свои вызовы createElement на createElementNS, и теперь все работает! – kitsu.eb 12 July 2013 в 22:01
  • 2
    Вы можете манипулировать элементами SVG с помощью jquery, как только они создаются с помощью метода DOM createElementNS. Вы можете изменить функцию makeSVG на «return $ (el)», и теперь у вас есть элемент svg, который может использовать методы jquery. – Hoffmann 28 November 2013 в 16:56
  • 3
    Ах! Вот почему это не сработает. Я пробовал то же самое с двумя разными библиотеками: один в jQuery и один в D3.js . Я получил ровно один и тот же исходный вывод в HTML с использованием обоих, но создаваемые jQuery элементы не отображались, пока выполнялись D3-сгенерированные! Я рекомендую использовать D3: d3.select('body').append('svg').attr('width','100%'); – chharvey 15 December 2014 в 22:36
  • 4
    @MadsSkjern: DOM Level 3 LS никогда не попадал в браузеры. Первоначально Mozilla-only DOMParser теперь более широко поддерживается, и jQuery имеет $.parseXML. – bobince 19 July 2015 в 20:27
  • 5
    Это по-прежнему актуально. bobince ненавидит беспорядок HTML5, я ненавижу беспорядок всей сети, потому что нигде вы не можете найти хороший код без хаков из-за этого беспорядка. WWW следует переименовать в WWM World Wide Mess. – Olivier Pons 7 April 2016 в 12:28
 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.
0
ответ дан 4baad4 19 August 2018 в 06:07
поделиться

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

10
ответ дан Chris Dolphin 19 August 2018 в 06:07
поделиться
  • 1
    Ответ Тимо (проголосовавший сверху) работал в Chrome, но не в IE. Этот ответ решил проблему для обоих браузеров! – CoderDennis 20 March 2018 в 19:45
  • 2
    – Chris Dolphin 21 March 2018 в 01:52

Принимаемый ответ показывает слишком сложный способ. Как утверждает Форресто в его ответе , « он, кажется, добавляет их в 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 (). И когда элемент (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()); не работает при использовании Рафаэля. Это будет очень короткий взлом.)

122
ответ дан Community 19 August 2018 в 06:07
поделиться
  • 1
    Спасибо, ваш подход слишком сработал. – ismail baig 28 November 2013 в 08:11
  • 2
    Я использую мое (домашнее) решение для обработки SVG и имеет ту же проблему, что и Рафаэль, при использовании трюка «reparsing». с $ (# picture) .html ..., , но мое решение состояло в том, чтобы перезапустить некоторые кешированные элементы SVG (маркировка прямоугольника, трансформации и т. д.), – halfbit 12 January 2014 в 20:10
  • 3
    Вы волшебник. Я смотрел на принятый ответ, и я был как человек, это будет больно. Затем я увидел это, и он сделал все, что мне нужно, в одной короткой строке. Спасибо! – The Composer 1 May 2014 в 20:51
  • 4
    ЭТО БЫЛО ПРИЧИНАТЬ МНЕ, ЧТОБЫ ПОТЕРЯТЬ МОИ МАРБЛЫ ... подумал, что могу магически использовать js DOM elms в пространстве имен SVG, как можно было бы подумать ... получил инспектор тегов, чтобы распознать вставки ... но не кубик до сих пор! – Gus Crawford 28 June 2014 в 15:52
  • 5
    Работала отлично для меня, добавляя мои существующие теги полигона и пути к вновь созданному родительскому тэгу svg. Благодаря! – Kivak Wolf 29 January 2015 в 18:45

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

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

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

$('.svg_element').unwrap();
1
ответ дан Dan185 19 August 2018 в 06:07
поделиться

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/

23
ответ дан forresto 19 August 2018 в 06:07
поделиться
  • 1
    Спасибо, очень помог! Это очень хороший подход. Вместо добавления отдельного <circle> или других элементов с использованием сложных и медленных DOM-методов (например, createDocumentFragment() или createElementNS()) добавьте весь документ svg в контейнер. Вместо $ ('body') это может быть, конечно, некоторый $ ('div'). И после этого документ svg находится в DOM и может быть запрошен знакомым способом, например, с помощью. $ ( 'С'). Атр ( 'заливка', 'красный'). – Timo Kähkönen 3 November 2012 в 10:59
  • 2
    – Timo Kähkönen 3 November 2012 в 11:47
  • 3
    – Dustin Poissant 2 June 2016 в 22:18
  • 4
    Это был лучший ответ для меня. Мне нужен svg с помощью & lt; use xlink: href = "# icon & quot; & gt; & gt; & gt; и это был самый короткий рабочий ответ. – Eydamos 28 June 2016 в 12:59

Если строка, которую вам нужно добавить, это 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))
4
ответ дан jdesjean 19 August 2018 в 06:07
поделиться

На основании ответа @ 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);
5
ответ дан Jonas Berlin 19 August 2018 в 06:07
поделиться
  • 1
    Конечно, вы также можете: $svg.append($s('<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red"/>')); – Jonas Berlin 29 September 2016 в 14:09

Нашел простой способ, который работает со всеми браузерами (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>

6
ответ дан Matthieu Charbonnier 19 August 2018 в 06:07
поделиться

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

-1
ответ дан maytham-ɯɐɥʇʎɐɯ 19 August 2018 в 06:07
поделиться
  • 1
    не работает для меня, пытался это пару раз – Bhagya 20 May 2015 в 09:02

Все более популярная библиотека 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");
35
ответ дан nategood 19 August 2018 в 06:07
поделиться
  • 1
    хороший ответ. это помогло мне в этом вопросе. – ismail baig 16 October 2014 в 05:43
  • 2
    jQuery также задыхается при настройке / получении классов из SVG. Просто в стороне. – QueueHammer 1 December 2014 в 23:53
  • 3
    Спасибо! Хороший ответ! – Nat Davydova 30 April 2018 в 12:42

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

Делает:

-1
ответ дан paul_h 19 August 2018 в 06:07
поделиться
  • 1
    Это не элементы SVG, которые вы добавляете, это текстовое содержимое. – Robert Longson 6 January 2018 в 00:05
  • 2
    Это может быть, но это сделало после начальной загрузки страницы статически встроенного SVG. – paul_h 7 January 2018 в 14:51
  • 3
    Итак, что же, вопрос не в том, что касается элементов svg, а не текстового контента. – Robert Longson 7 January 2018 в 15:43
  • 4
    $(this).clone() клонирует элемент SVG (и это дети, если он есть). Посмотрите на использование text(). Я беру один tspan, у которого была «Your New & quot; в нем, и заканчивая двумя tspans, один с «Your & quot; в нем (черный) и один с «Новым в нем». (синий с линиями). Jeez, от 0 голосов до -1 :-( – paul_h 8 January 2018 в 16:09

Принятый ответ Бобинса - это короткое портативное решение. Если вам нужно не только добавлять 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');
3
ответ дан Premasagar 19 August 2018 в 06:07
поделиться

Я могу видеть круг в 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>
7
ответ дан Topera 19 August 2018 в 06:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: