4

I can modify xlink:href through javascript and jquery just fine but modifying just the dom's xlink:href through the knockout attr binding does not work.

This is my svg definition

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none">
<defs>
    <symbol id="icon-home" viewBox="0 0 32 32">
        <path class="path1" d="M32 18.451l-16-12.42-16 12.42v-5.064l16-12.42 16 12.42zM28 18v12h-8v-8h-8v8h-8v-12l12-9z"></path>
    </symbol>
</defs>

This is inserted at the top of the body

Then use knockout with html and the property icon on my view model

<svg class="svg-icon">
    <use id="myuse" data-bind="attr: {'xlink:href': icon }"></use>
</svg>

I am sure icon is returning correctly because I get the following rendered output

<svg class="svg-icon">
    <use data-bind="attr: {'xlink:href': icon }" xlink:href="#icon-home"></use>
</svg>

Which is correct, but nothing shows up. Does anyone have a working solution to this with knockout?

GôTô
  • 7,974
  • 3
  • 32
  • 43
nano2nd
  • 333
  • 3
  • 10
  • Maybe this thread will help http://stackoverflow.com/questions/3642035/jquerys-append-not-working-with-svg-element – GôTô Jul 28 '14 at 15:00

2 Answers2

17

As it seems, SVG will not update on DOM's modification.

So basically what you need to do is remove the SVG, update the fields, and add the SVG markup again.

This can be emulated with an if binding:

<!-- ko if: showSvg -->
<svg class="svg-icon">
    <use data-bind="attr: {'xlink:href': icon }"></use>
</svg>
<!-- /ko -->

When showSvg becomes false, your SVG will be removed from the DOM, and added again when it becomes true.

You would do:

myModel.showSvg(false);
myModel.icon("whatever");
myModel.showSvg(true);

Or more ko compliant, use a writeable computed to encapsulate this behaviour:

myModel.iconComp = ko.computed({
    read: myModel.icon,
    write: function (value) {
        myModel.showSvg(false);
        myModel.icon(value);
        myModel.showSvg(true);
    },
    owner: this
});

And use iconComp in your markup instead of icon.

UPDATE

Ok, forget everything I said...

It works if you define your attribute first (tested with last Chrome and IE):

<svg class="svg-icon">
    <use data-bind="attr:{ 'xlink:href': icon }" xlink:href=''></use>
    <!-- Add the attr you want to bind to, set it blank like this for example-->
</svg>

Then do your normal binding.

Demo

Community
  • 1
  • 1
GôTô
  • 7,974
  • 3
  • 32
  • 43
  • I created an example in JSFiddle that I believe is doing just what you said, but still no success. It would be fantastic if someone could help me get a working fiddle.http://jsfiddle.net/joeyavant/8taCQ/ – nano2nd Jul 28 '14 at 19:56
  • Hmm, using an external svg file with svg4everyone this doesn't seem to work sadly – Sam Jan 04 '17 at 13:54
  • @Sam have you tried the solution written before the **Update** title of my answer? – GôTô Jan 04 '17 at 14:00
  • @GôTô I have yes, to no avail I'm afraid. Could be that I have done something wrong but after a lot of trial and error I've decided to go with internal svg, works like a charm and less hassle, it's for an SPA anyways. – Sam Jan 05 '17 at 14:13
  • @Sam as long as you have a solution then great. That's from a long time ago and I barely remember it to be honest :) – GôTô Jan 05 '17 at 14:16
0

Goto, Your first assumption regarding SVG will not update on DOM modification is not entirely true. I have an entire SVG web application built with knockout and we have anything from path to circles and images which are bound to View Models. These graphics do change without any issues in new browsers such as latest versions of Chrome and Firefox. In our case we're not too concerned about older versions of IE, so we haven't done sufficient testing in that front.

But this is mostly a browser engine issue rather than 'SVG' technology. Different engines are very likely to respond differently.

You're final edit is correct. You need to have the attribute in there, but I suggest putting a placeholder value. What I use is :

<image data-bind="attr:{'xlink:href':icon.href,x:icon.x,y:icon.y,icon.height,width:icon.width}" xlink:href="whitedot.png"></image>

I've created whitedot.png as a 1x1 pixel white dot which appears initially until the binding takes place. This is probably a good idea over leaving it empty to avoid getting the image missing replacement icon you sometimes see in Firefox or Chrome until your actual image get loaded.

Shakus
  • 421
  • 5
  • 8