0

This question and this question are similar, but the answers do not yield a clean, consistent method for finding the top most SVG element among different HTML strings.

The goal is to extract the top-most SVG element from a HTML string.

$(htmlString).find("svg") does not work.

$($.parseHTML(htmlString)) only generates an array of jQuery objects, but the goal is to turn the htmlString into one jQuery object where you can execute find and retrieve the top-most SVG element.

Example HTML string:

<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424" style="enable-background:new 0 0 424 424;" xml:space="preserve" width="512px" height="512px">
<g>
    <g>
        <path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
        <path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
        <path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
    </g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

Result of $($.parseHTML(htmlString)), where htmlString is the string above:

$($.parseHTML(htmlString))

w.fn.init(6) [comment, text, comment, text, svg#Capa_1, text]
0: comment
1: text
2: comment
3: text
4: svg#Capa_1
5: text
length: 6
Crashalot
  • 33,605
  • 61
  • 269
  • 439

1 Answers1

3

You cannot use find on the result because the markup you give to jQuery doesn't represent a tree: Doctype and comments nodes are siblings of your <svg>.

Thus, you need filter the jQuery entries in order to keep only the svg node:

const $svg = $(`<?xml version="1.0" encoding="utf-8"?>
<!-- comment1  -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424">
  <g>
    <g>
      <path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
      <path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
      <path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
    </g>
  </g>
</svg>`)
.filter((i, el) => $(el).is('svg'));

console.log($svg.attr('id'));
$svg.find('path').attr('fill', 'red');
$('body').append($svg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Now, you may also want to use a native DOMParser, which might be better at handling namespaces than jQuery. From there, you will have an XMLDocument, whose documentElement will be your <svg> node. You will then be able to work on it from jQuery:

const str = `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 424 424">
  <g>
    <g>
      <path d="M35,89C15,89,0,74,0,54s15-36,35-36h353c20,0,36,16,36,36s-16,35-36,35H35z" fill="#7c7a7d"/>
      <path d="M388,176c20,0,36,16,36,36s-16,35-36,35H35c-20,0-35-15-35-35s15-36,35-36H388z" fill="#7c7a7d"/>
      <path d="M388,335c20,0,36,15,36,35s-16,36-36,36H35c-20,0-35-16-35-36s15-35,35-35H388z" fill="#7c7a7d"/>
    </g>
  </g>
</svg>`;

const doc = (new DOMParser).parseFromString(str, 'image/svg+xml');
// use the second argument (context) of jQuery()
const $svg = $('svg', doc);
console.log($svg.attr('id'));
$svg.find('path').attr('fill', 'red');
$('body').append($svg);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Kaiido
  • 123,334
  • 13
  • 219
  • 285