0

我正在尝试使用 knockoutJS 动态渲染一些 SVG。换句话说,一个 ajax 响应返回了有效的 SVG,我希望 svgweb 动态显示它。


注意- 这个问题是用回答你自己的问题功能编写和回答的,所以我希望可以为下一个人节省一个小时左右的搜索时间。

4

1 回答 1

1

本答案中所述,您需要使用svgweb 公开的appendChild方法。请注意,它接受一个实际的节点,而不仅仅是文本。

为方便起见,您可以剥离来自 ajax 响应的任何<xml>或节点。<doctype>然后,用 jQuery 包装剥离的文本后,结果的第 0 个节点应该是可以传递给的有效 svg 节点appendChild

下面的淘汰赛扩展包了这个功能。

ko.bindingHandlers.renderSvg = {
    init: renderSvg,
    update: renderSvg
};

function renderSvg(element, valueAccessor, allBindingsAccessor, viewModel) {
    var rawVal = valueAccessor();
    var svgText = ko.utils.unwrapObservable(rawVal);

    if (!svgText) {
        element.innerHTML = '';
    } else {
        //clear out previous content
        element.innerHTML = '';

        //strip out any `<xml>` or <!doctype> tags that come over
        if (svgText.indexOf('<svg') > 0){
            svgText = svgText.substr(svgText.indexOf('<svg'));
        }
        window.svgweb.appendChild($(svgText)[0], element);
    }
};

当然是这样调用的:

<div data-bind="renderSvg: mySvgField"></div>

编辑

事实证明,用 jQuery 包装该 SVG 字符串,并尝试附加结果会导致问题。我发现的解决方法是用 jQuery 包装父级 $svg,然后使用 jQuery 循环遍历所有子级。一个简单的 1 级搜索对我来说就足够了。显然更复杂的用例需要递归搜索。更新后的代码如下。

function renderSvg(element, valueAccessor, allBindingsAccessor, viewModel) {
    var rawVal = valueAccessor();
    var svgText = ko.utils.unwrapObservable(rawVal);

    if (!svgText) {
        element.innerHTML = '';
    } else {
        element.innerHTML = '';

        if (svgText.indexOf('<svg') > 0){
            svgText = svgText.substr(svgText.indexOf('<svg'));
        }

        if (!$.browser.msie || $.browser.version > 8){
            //normal browsers
            window.svgweb.appendChild($(svgText)[0], element);
        } else {
            //IE 8
            var $svg = $(svgText);
            var svg = document.createElementNS(svgns, 'svg');

            svg.setAttribute('width', $svg.attr('width'));
            svg.setAttribute('height', $svg.attr('height'));

            $.each($svg.children(), function(i, el){
                var path = document.createElementNS(svgns, el.tagName);

                for (var i = 0, allAttributes = el.attributes, len = allAttributes.length; i < len; i++){
                    path.setAttribute(allAttributes.item(i).nodeName, allAttributes.item(i).nodeValue);
                }
                svg.appendChild(path);
            });
            window.svgweb.appendChild(svg, element);
        }
    }
}
于 2013-10-12T20:05:25.690 回答