【问题标题】:jQuery SVG, why can't I addClass?jQuery SVG,为什么我不能添加类?
【发布时间】:2012-01-28 03:29:35
【问题描述】:

我正在使用 jQuery SVG。我无法向对象添加或删除类。有人知道我的错误吗?

SVG:

<rect class="jimmy" id="p5" x="200" y="200" width="100" height="100" />

不添加类的jQuery:

$(".jimmy").click(function() {
    $(this).addClass("clicked");
});

我知道 SVG 和 jQuery 可以很好地协同工作,因为我可以定位对象并在单击时触发警报:

$(".jimmy").click(function() {
    alert('Handler for .click() called.');
});

【问题讨论】:

  • 好文章在这里:toddmotto.com/…
  • 所以 WHY can't I use the jQuery *class functions 的实际问题仍未得到解答...如果我可以通过 jQuery attr 函数访问 SVG 元素的属性,为什么 *class 函数也不能这样做? jQuery 失败?!?
  • 说真的,有人知道WHY吗??
  • 为 SVG 文件添加此功能的小库:github.com/toddmotto/lunar
  • “为什么”是因为 jQuery 使用“className”属性,它是 HTML 元素的字符串属性,但 SVG 元素是 SVG 动画字符串。不能将其分配给 HTML 元素的 like。因此,jQuery 代码在尝试分配值时崩溃了。

标签: javascript jquery css svg jquery-svg


【解决方案1】:

编辑 2016:阅读接下来的两个答案。

  • JQuery 3 修复了根本问题
  • Vanilla JS:element.classList.add('newclass') 适用于现代浏览器

JQuery(小于 3)无法将类添加到 SVG。

.attr() 适用于 SVG,所以如果你想依赖 jQuery:

// Instead of .addClass("newclass")
$("#item").attr("class", "oldclass newclass");
// Instead of .removeClass("newclass")
$("#item").attr("class", "oldclass");

如果你不想依赖 jQuery:

var element = document.getElementById("item");
// Instead of .addClass("newclass")
element.setAttribute("class", "oldclass newclass");
// Instead of .removeClass("newclass")
element.setAttribute("class", "oldclass");

【讨论】:

  • 支持 forresto 的建议 - discusscode.blogspot.in/2012/08/…
  • 完美。这应该是答案。虽然真正的答案是让 jquery 默认包含这个,至少作为设置或其他东西。
  • .attr("class", "oldclass")(或者更繁琐的.setAttribute("class", "oldclass"))真的不等于去掉newclass
  • 很好的答案(和 v.nice 技术)......但编辑问题是否是一个想法,以便它首先声明 jquery 无法将类添加到 SVG(如果这是情况……看起来)?
  • 只是一个附录:我试过了,JQuery 3 没有解决这个问题。
【解决方案2】:

jQuery 3 没有这个问题

the jQuery 3.0 revisions 上列出的更改之一是:

添加 SVG 类操作(#219920aaed3

此问题的一个解决方案是升级到 jQuery 3。效果很好:

var flip = true;
setInterval(function() {
    // Could use toggleClass, but demonstrating addClass.
    if (flip) {
        $('svg circle').addClass('green');
    }
    else {
        $('svg circle').removeClass('green');
    }
    flip = !flip;
}, 1000);
svg circle {
    fill: red;
    stroke: black;
    stroke-width: 5;
}
svg circle.green {
    fill: green;
}
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>

<svg>
    <circle cx="50" cy="50" r="25" />
</svg>

问题:

jQuery 类操作函数不适用于 SVG 元素的原因是 3.0 之前的 jQuery 版本对这些函数使用 className 属性。

摘自 jQuery attributes/classes.js:

cur = elem.nodeType === 1 && ( elem.className ?
    ( " " + elem.className + " " ).replace( rclass, " " ) :
    " "
);

这与 HTML 元素的预期行为一样,但对于 SVG 元素 className 有点不同。对于 SVG 元素,className 不是字符串,而是SVGAnimatedString 的一个实例。

考虑以下代码:

var test_div = document.getElementById('test-div');
var test_svg = document.getElementById('test-svg');
console.log(test_div.className);
console.log(test_svg.className);
#test-div {
    width: 200px;
    height: 50px;
    background: blue;
}
<div class="test div" id="test-div"></div>

<svg width="200" height="50" viewBox="0 0 200 50">
  <rect width="200" height="50" fill="green" class="test svg" id="test-svg" />
</svg>

如果您运行此代码,您将在开发者控制台中看到类似以下内容。

test div
SVGAnimatedString { baseVal="test svg",  animVal="test svg"}

如果我们像 jQuery 那样将 SVGAnimatedString 对象转换为字符串,我们将得到 [object SVGAnimatedString],这就是 jQuery 失败的地方。

jQuery SVG 插件如何处理这个问题:

jQuery SVG 插件通过修补相关函数以添加 SVG 支持来解决此问题。

摘自 jQuery SVG jquery.svgdom.js:

function getClassNames(elem) {
    return (!$.svg.isSVGElem(elem) ? elem.className :
        (elem.className ? elem.className.baseVal : elem.getAttribute('class'))) || '';
}

此函数将检测元素是否为 SVG 元素,如果是,它将使用 SVGAnimatedString 对象的 baseVal 属性(如果可用),然后再使用 class 属性。

jQuery 在这个问题上的历史立场:

jQuery 目前在他们的Won’t Fix 页面上列出了这个问题。这是相关部分。

SVG/VML 或命名空间元素错误

jQuery 主要是一个用于 HTML DOM 的库,因此大多数与 SVG/VML 文档或命名空间元素相关的问题都超出了范围。我们确实尝试解决“渗透”到 HTML 文档的问题,例如从 SVG 冒出的事件。

显然 jQuery 认为完全 SVG 支持超出了 jQuery 核心的范围,并且更适合插件。

【讨论】:

    【解决方案3】:

    我在我的项目中写了这个,它可以工作......可能;)

    $.fn.addSvgClass = function(className) {
    
        var attr
        this.each(function() {
            attr = $(this).attr('class')
            if(attr.indexOf(className) < 0) {
                $(this).attr('class', attr+' '+className+ ' ')
            }
        })
    };    
    
    $.fn.removeSvgClass = function(className) {
    
        var attr
        this.each(function() {
            attr = $(this).attr('class')
            attr = attr.replace(className , ' ')
            $(this).attr('class' , attr)
        })
    };    
    

    例子

    $('path').addSvgClass('fillWithOrange')
    $('path').removeSvgClass('fillWithOrange')
    

    【讨论】:

      【解决方案4】:

      或者,当 JQ 中间某处有一只猴子时,就使用老式的 DOM 方法。

      var myElement = $('#my_element')[0];
      var myElClass = myElement.getAttribute('class').split(/\s+/g);
      //splits class into an array based on 1+ white space characters
      
      myElClass.push('new_class');
      
      myElement.setAttribute('class', myElClass.join(' '));
      
      //$(myElement) to return to JQ wrapper-land
      

      了解 DOM 人员。即使在 2016 年的 framework-palooza 中,它也很有帮助。另外,如果你听到有人将 DOM 比作程序集,请替我踢他们。

      【讨论】:

        【解决方案5】:

        一种解决方法可能是将类添加到 svg 元素的容器中:

        $('.svg-container').addClass('svg-red');
        .svg-red svg circle{
            fill: #ED3F32;
        }
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
        <div class="svg-container">
          <svg height="40" width="40">
              <circle cx="20" cy="20" r="20"/>
          </svg>
        </div>

        【讨论】:

          【解决方案6】:

          受上述答案的启发,尤其是受到 Sagar Gala 的启发,我创建了这个API。如果您不想或无法升级您的 jquery 版本,您可以使用它。

          【讨论】:

            【解决方案7】:

            基于上述答案,我创建了以下 API

            /*
             * .addClassSVG(className)
             * Adds the specified class(es) to each of the set of matched SVG elements.
             */
            $.fn.addClassSVG = function(className){
                $(this).attr('class', function(index, existingClassNames) {
                    return ((existingClassNames !== undefined) ? (existingClassNames + ' ') : '') + className;
                });
                return this;
            };
            
            /*
             * .removeClassSVG(className)
             * Removes the specified class to each of the set of matched SVG elements.
             */
            $.fn.removeClassSVG = function(className){
                $(this).attr('class', function(index, existingClassNames) {
                    var re = new RegExp('\\b' + className + '\\b', 'g');
                    return existingClassNames.replace(re, '');
                });
                return this;
            };
            

            【讨论】:

            • 这很有帮助,但算法存在问题: 1. 如果没有现有的类字符串,则在添加类时会得到“未定义的类名”。 2. 如果类字符串包含作为正则表达式超集的类,则在类字符串中留下垃圾。例如,如果您尝试删除类“dog”并且类字符串包含“dogfood”,那么类字符串中将留下“food”。以下是一些修复:jsfiddle.net/hellobrett/c2c2zfto
            • 这不是一个完美的解决方案。它替换所有包含您要删除的类的单词。
            【解决方案8】:

            jQuery 2.2 支持 SVG 类操作

            jQuery 2.2 and 1.12 Released 帖子包含以下引用:

            虽然 jQuery 是一个 HTML 库,但我们同意对 SVG 元素的类支持可能很有用。用户现在可以调用 .addClass().removeClass().toggleClass().hasClass () 方法在 SVG 上。 jQuery 现在更改类属性而不是 className 属性。这也使得类方法可用于一般 XML 文档。请记住,许多其他事情不适用于 SVG,如果您需要除类操作之外的任何内容,我们仍然建议您使用专用于 SVG 的库。

            使用jQuery 2.2.0 的示例

            它测试:

            • .addClass()
            • .removeClass()
            • .hasClass()

            如果你点击那个小方块,它会改变它的颜色,因为class 属性被添加/删除了。

            $("#x").click(function() {
                if ( $(this).hasClass("clicked") ) {
                    $(this).removeClass("clicked");
                } else {
                    $(this).addClass("clicked");
                }
            });
            .clicked {
                fill: red !important;  
            }
            <html>
            
            <head>
                <script src="https://code.jquery.com/jquery-2.2.0.js"></script>
            </head>
            
            <body>
                <svg width="80" height="80">
                    <rect id="x" width="80" height="80" style="fill:rgb(0,0,255)" />
                </svg>
            </body>
            
            </html>

            【讨论】:

              【解决方案9】:

              这是我处理以下问题的相当不雅但有效的代码(没有任何依赖关系):

              • classList 不存在于 IE 中的 &lt;svg&gt; 元素上
              • className 不代表 IE 中 &lt;svg&gt; 元素上的 class 属性
              • 旧 IE 的 getAttribute()setAttribute() 实现已损坏

              它尽可能使用classList

              代码:

              var classNameContainsClass = function(fullClassName, className) {
                  return !!fullClassName &&
                         new RegExp("(?:^|\\s)" + className + "(?:\\s|$)").test(fullClassName);
              };
              
              var hasClass = function(el, className) {
                  if (el.nodeType !== 1) {
                      return false;
                  }
                  if (typeof el.classList == "object") {
                      return (el.nodeType == 1) && el.classList.contains(className);
                  } else {
                      var classNameSupported = (typeof el.className == "string");
                      var elClass = classNameSupported ? el.className : el.getAttribute("class");
                      return classNameContainsClass(elClass, className);
                  }
              };
              
              var addClass = function(el, className) {
                  if (el.nodeType !== 1) {
                      return;
                  }
                  if (typeof el.classList == "object") {
                      el.classList.add(className);
                  } else {
                      var classNameSupported = (typeof el.className == "string");
                      var elClass = classNameSupported ?
                          el.className : el.getAttribute("class");
                      if (elClass) {
                          if (!classNameContainsClass(elClass, className)) {
                              elClass += " " + className;
                          }
                      } else {
                          elClass = className;
                      }
                      if (classNameSupported) {
                          el.className = elClass;
                      } else {
                          el.setAttribute("class", elClass);
                      }
                  }
              };
              
              var removeClass = (function() {
                  function replacer(matched, whiteSpaceBefore, whiteSpaceAfter) {
                      return (whiteSpaceBefore && whiteSpaceAfter) ? " " : "";
                  }
              
                  return function(el, className) {
                      if (el.nodeType !== 1) {
                          return;
                      }
                      if (typeof el.classList == "object") {
                          el.classList.remove(className);
                      } else {
                          var classNameSupported = (typeof el.className == "string");
                          var elClass = classNameSupported ?
                              el.className : el.getAttribute("class");
                          elClass = elClass.replace(new RegExp("(^|\\s)" + className + "(\\s|$)"), replacer);
                          if (classNameSupported) {
                              el.className = elClass;
                          } else {
                              el.setAttribute("class", elClass);
                          }
                      }
                  }; //added semicolon here
              })();
              

              示例用法:

              var el = document.getElementById("someId");
              if (hasClass(el, "someClass")) {
                  removeClass(el, "someClass");
              }
              addClass(el, "someOtherClass");
              

              【讨论】:

              • 你用IE7测试过吗?因为 LTE IE7在设置、获取或删除 CLASS 属性时要求 strAttributeName 为“className”
              • @Knu:它绝对适用于 IE 6 和 7 中的常规 HTML 元素,因为在那些浏览器中它使用 className 属性而不是尝试设置属性。 “在设置、获取或删除 CLASS 属性时,LTE IE7 要求 strAttributeName 为“className””的原因是那些浏览器对 getAttribute(x)setAttribute(x) 的实现有缺陷,它们只是获取或设置名称为 @ 的属性987654334@,所以直接设置属性更简单,更兼容。
              • @Knu:好的。我不确定,因为 IE 7 不支持 SVG,所以我不确定您希望通过在设计用于 IE 7 的页面中包含 &lt;svg&gt; 元素来实现什么。对于它的价值,我的代码确实向 IE 7 中的 &lt;svg&gt; 元素成功添加了一个类属性,因为这样的元素的行为与任何其他自定义元素一样。
              • 完全忘记了,谢谢提醒。我会尝试使用 Adob​​e SVG Viewer 来检查它是否按预期工作。
              【解决方案10】:

              我使用 Snap.svg 向 SVG 添加一个类。

              var jimmy = Snap(" .jimmy ")
              
              jimmy.addClass("exampleClass");
              

              http://snapsvg.io/docs/#Element.addClass

              【讨论】:

              • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接的答案可能会失效。
              【解决方案11】:

              只需将缺少的原型构造函数添加到所有 SVG 节点:

              SVGElement.prototype.hasClass = function (className) {
                return new RegExp('(\\s|^)' + className + '(\\s|$)').test(this.getAttribute('class'));
              };
              
              SVGElement.prototype.addClass = function (className) { 
                if (!this.hasClass(className)) {
                  this.setAttribute('class', this.getAttribute('class') + ' ' + className);
                }
              };
              
              SVGElement.prototype.removeClass = function (className) {
                var removedClass = this.getAttribute('class').replace(new RegExp('(\\s|^)' + className + '(\\s|$)', 'g'), '$2');
                if (this.hasClass(className)) {
                  this.setAttribute('class', removedClass);
                }
              };
              

              你可以这样使用它而不需要 jQuery:

              this.addClass('clicked');
              
              this.removeClass('clicked');
              

              所有功劳归Todd Moto

              【讨论】:

              【解决方案12】:

              jQuery 不支持 SVG 元素的类。您可以直接$(el).get(0) 获取元素并使用classListadd / remove。这也有一个技巧,最顶层的 SVG 元素实际上是一个普通的 DOM 对象,并且可以像 jQuery 中的所有其他元素一样使用。在我的项目中,我创建了这个来处理我需要的东西,但是Mozilla Developer Network 上提供的文档有一个可以用作替代方案的垫片。

              example

              function addRemoveClass(jqEl, className, addOrRemove) 
              {
                var classAttr = jqEl.attr('class');
                if (!addOrRemove) {
                  classAttr = classAttr.replace(new RegExp('\\s?' + className), '');
                  jqEl.attr('class', classAttr);
                } else {
                  classAttr = classAttr + (classAttr.length === 0 ? '' : ' ') + className;
                  jqEl.attr('class', classAttr);
                }
              }
              

              另一种更严格的方法是使用 D3.js 作为您的选择器引擎。我的项目有用它构建的图表,所以它也在我的应用程序范围内。 D3 正确修改了 vanilla DOM 元素和 SVG 元素的类属性。虽然为这种情况添加 D3 可能有点过头了。

              d3.select(el).classed('myclass', true);
              

              【讨论】:

              • 感谢 d3 位...我实际上已经在页面上有 d3 所以决定使用它。非常有用:)
              【解决方案13】:

              如果您有动态类或不知道可以应用哪些类,那么我认为这种方法是最好的方法:

              // addClass
              $('path').attr('class', function(index, classNames) {
                  return classNames + ' class-name';
              });
              
              // removeClass
              $('path').attr('class', function(index, classNames) {
                  return classNames.replace('class-name', '');
              });
              

              【讨论】:

              • 这对我来说是救命稻草,因为我需要更改很多 SVG 元素才能完成工作。 +999 来自我 :)
              • 很难相信 jQuery 仍然会在这方面深耕细作,即使在 2 年和 2.x.x 发布之后。你的修复,导航,为我节省了一天。这些修复的其余部分是不必要的复杂。谢谢!
              【解决方案14】:

              加载jquery.svg.js后,你必须加载这个文件:http://keith-wood.name/js/jquery.svgdom.js

              来源:http://keith-wood.name/svg.html#dom

              工作示例:http://jsfiddle.net/74RbC/99/

              【讨论】:

              • 添加了它 - 似乎仍然不起作用。我一直在浏览 jquery SVG 网站上的文档,但没有明确说明您需要做什么才能使用它的额外功能。
              【解决方案15】:

              DOM API 中有 element.classList 可用于 HTML 和 SVG 元素。不需要 jQuery SVG 插件甚至 jQuery。

              $(".jimmy").click(function() {
                  this.classList.add("clicked");
              });
              

              【讨论】:

              • 我对此进行了测试,并且 .classList 似乎未定义 svg 子元素,至少在 Chrome 中是这样。
              • 在 Chrome 中为我工作。我在 HTML 中嵌入了 SVG,所以我的文档结构如下所示:
              • 我用它在 SVG 元素中添加类,在 Chrome 中运行良好。
              • IE 没有在 SVG 元素上实现 .classList 和 API。请参阅caniuse.com/#feat=classlist 和“已知问题”列表。这也提到了 WebKit 浏览器。
              • 随着时间的推移,这个答案变得更加正确(也是最佳答案)
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2020-10-06
              相关资源
              最近更新 更多