d3.js SVG foreignObject内容不显示,除非是纯文本

kcrjzv8t  于 2023-10-19  发布在  其他
关注(0)|答案(6)|浏览(369)

我试图在SVG绘图中使用foreignObject标记输出HTML。我使用d3来生成元素。只有当foreignObject标签中的内容是纯文本时,才会显示foreignObject标签中的HTML内容,否则它只显示为空/空白。请看这个jsfiddle,看看我的问题的一个例子:http://jsfiddle.net/R9e3Y/29/
foreignObject标记中的内容在检查元素this this时显示:

  1. <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  2. <foreignObject x="40" y="40" width="100" height="100">
  3. <div>test</div>
  4. </foreignObject>
  5. </svg>

但在屏幕上看不到的东西吗如何让内容显示出来?

uinbv5nw

uinbv5nw1#

请确保设置了widthheight参数。
添加xmlns并没有为我解决问题。结果对我来说问题更简单...我没有添加widthheight参数,所以foreignObject内部的内容没有显示,即使它在那里。这两个参数似乎默认为0。

7gs2gvoe

7gs2gvoe2#

除了@robert-longson的回答:
如果你使用d3selection.join(),它看起来像这样:

  1. myForeignObject.selectAll('div').data(myData).join('xhtml:div')

请注意,xhtml命名空间仅在join()中指定,而在selectAll()中没有指定。

l2osamch

l2osamch3#

我为此开发了一些东西。代码在下面。更高级的版本使用代理来拉入外部非CORS资源。svg foreignObject阻止加载任何非CORS跨域请求。我写了一个简单的代理在Runkit上运行。看下面。
其局限性是:没有外部非CORS字体,没有非CORS图像。任何想要帮助改进这一点的人,包括添加对图像和字体的支持,都可以在这里做出贡献:https://github.com/dosyago-coder-0/dompeg.js/blob/master/dompeg.js

  • 网页脚本:*
  1. (async function (){
  2. const width = document.scrollingElement.scrollWidth;
  3. const height = document.scrollingElement.scrollHeight;
  4. const doc = document.implementation.createHTMLDocument('');
  5. doc.write(document.documentElement.outerHTML);
  6. doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
  7. const styles = [];
  8. for( let i = 0; i < document.styleSheets.length; i++ ) {
  9. const ss = document.styleSheets[i];
  10. if ( ss.cssRules ) {
  11. for( let j = 0; j < ss.cssRules.length; j++ ) {
  12. styles.push( ss.cssRules[j].cssText );
  13. }
  14. } else {
  15. try {
  16. const res = await fetch(ss.href);
  17. const cssText = await res.text();
  18. styles.push(cssText);
  19. } catch(e) {
  20. /** fetch to proxy here as fallback
  21. * uncomment if you set up your proxy server
  22. try {
  23. const res = await fetch(`https://${YOUR PROXY SERVER}.runkit.sh/?url=${btoa(ss.href)}`);
  24. const cssText = await res.text();
  25. styles.push(cssText);
  26. } catch(e) { **/
  27. console.warn(`Exception adding styles from ${ss.href}`, e, e.stack);
  28. /** uncomment if you setup proxy
  29. }
  30. **/
  31. }
  32. }
  33. }
  34. Array.from( doc.querySelectorAll('noscript, link, script')).forEach( el => el.remove() );
  35. stripComments(doc);
  36. Array.from( doc.querySelectorAll('*[style]')).forEach( el => {
  37. const styleText = el.getAttribute('style');
  38. const uniq = (Math.random()+''+performance.now()).replace(/\./g,'x');
  39. const className = `class${uniq}`;
  40. const cssText = `.${className} {${ styleText }}`;
  41. styles.push( cssText );
  42. el.classList.add( className );
  43. });
  44. const styleElement = doc.createElement('style');
  45. styleElement.innerText = styles.join('\n');
  46. doc.documentElement.appendChild(styleElement);
  47. const canvas = document.createElement('canvas');
  48. Object.assign( canvas, {width,height});
  49. const ctx = canvas.getContext('2d');
  50. const data = `
  51. <svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
  52. <foreignObject width="100%" height="100%">
  53. ${(new XMLSerializer).serializeToString(doc).slice(15)}
  54. </foreignObject>
  55. </svg>`;
  56. const DOMURL = window.URL || window.webkitURL || window;
  57. const img = new Image();
  58. const svg = new Blob([data], {type: 'image/svg+xml'});
  59. Object.assign( img, {width,height});
  60. img.crossOrigin = "Anonymous";
  61. img.onload = function() {
  62. ctx.fillStyle = 'white';
  63. ctx.fillRect( 0, 0, canvas.width, canvas.height );
  64. ctx.drawImage(img, 0, 0);
  65. const datauri = canvas.toDataURL('image/jpeg');
  66. const anchor = document.createElement('a');
  67. anchor.download = 'screen.jpg';
  68. anchor.href = datauri;
  69. anchor.target = "_new";
  70. anchor.innerText = 'download screen.jpg';
  71. anchor.addEventListener('click', e => {e.stopPropagation();anchor.remove();}, { capture: true });
  72. document.body.appendChild(anchor);
  73. Object.assign( anchor.style, {
  74. position: 'fixed',
  75. background:'white',
  76. fontSize: '18px',
  77. fontFamily: 'monospace',
  78. color: 'blue',
  79. top: 0,
  80. left: 0,
  81. zIndex: Number.MAX_SAFE_INTEGER
  82. });
  83. }
  84. img.src = buildSvgImageUrl(data);
  85. img.style.position = "absolute";
  86. img.style.zIndex = "10000000";
  87. img.style.backgroundColor = "white";
  88. //document.body.appendChild(img);
  89. function buildSvgImageUrl(svg) {
  90. const b64 = btoa(unescape(encodeURIComponent(svg)));
  91. return "data:image/svg+xml;base64," + b64;
  92. }
  93. function stripComments(docNode){
  94. const commentWalker = docNode.evaluate('//comment()', docNode, null, XPathResult.ANY_TYPE, null);
  95. let comment = commentWalker.iterateNext();
  96. const cuts = [];
  97. while (comment) {
  98. cuts.push(comment);
  99. comment = commentWalker.iterateNext();
  100. }
  101. cuts.forEach( node => node.remove());
  102. }
  103. }());
  • runkit代理服务器脚本:*
  1. const request = require("request");
  2. const rp = require('request-promise');
  3. const {URL} = require('url');
  4. const express = require("@runkit/runkit/express-endpoint/1.0.0");
  5. const b64 = require('base-64');
  6. const bodyParser = require('body-parser');
  7. const page = (url,err) => `
  8. <form method=POST style="
  9. position: fixed;
  10. position: sticky;
  11. display: table;
  12. top: 0px;
  13. z-index:12000000;
  14. background: white;">
  15. <label for=hider99>X</label><input id=hider99 type=checkbox>
  16. <style>
  17. #hider99:checked ~ fieldset {
  18. display: none;
  19. }
  20. </style>
  21. <fieldset><legend>Proxy</legend>
  22. <p>
  23. <input required type=url size=62 name=url placeholder="any url" value="${url||'https://google.com/favicon.ico'}">
  24. <button style=background:lime>Load</button>
  25. ${ !! err ? `<p><span style=color:red>${err}</span>` : '' }
  26. </fieldset>
  27. </form>`;
  28. const app = express(module.exports);
  29. app.use(bodyParser.urlencoded({ extended: false }));
  30. app.get("/", async (req,res,next) => {
  31. console.log(req.query.url);
  32. let url;
  33. res.type('html');
  34. res.set('access-control-allow-origin', '*');
  35. try {
  36. url = b64.decode(req.query.url);
  37. new URL(url);
  38. } catch(e) { res.end(page('',"not a url"+e)); return; }
  39. try {
  40. res.type(type(url));
  41. const data = await rp(url);
  42. res.end(data);
  43. } catch(e) { res.end(page('',""+e)); }
  44. });
  45. app.get("/:anything", async (req,res,next) => {
  46. res.type('html');
  47. res.end('404 Not found');
  48. });
  49. function type(s = '') {
  50. return s.split(/\./g).pop() || 'html';
  51. }
  52. void 0;
展开查看全部
jjjwad0x

jjjwad0x4#

  1. var a = function(a) {
  2. var b = enter code heredocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
  3. b.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg">' + a + "</svg>";
  4. for (var c = document.createDocumentFragment(); b.firstChild.firstChild; ){
  5. c.appendChild(b.firstChild.firstChild);
  6. }
  7. return c;
  8. }
  9. $('#app_canvasContainer svg').append(a('<foreignObject overflow="visible" width="200" height="100" x="'+ x +'" y="'+ y +'" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><textarea class="text-entity" readonly="true" resizeable=false xmlns="http://www.w3.org/1999/xhtml" >'+ val +'</textarea>`enter code here`</foreignObject>'));
h9vpoimq

h9vpoimq5#

因为你使用的是d3,你需要告诉d3这个div是一个html div,而不是svg命名空间中的某个元素。尝试

  1. .append("xhtml:div")
xfyts7mz

xfyts7mz6#

<foreignObject>允许嵌入各种标记,而不仅仅是HTML。这意味着,必须有一种方法来确定使用什么语言。这就是名称空间发挥作用的地方。
要告诉SVG您拥有哪种foreignObject,您必须将内容放在适当的名称空间中。

  1. <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
  2. <foreignObject x="40" y="40" width="100" height="100">
  3. <div xmlns="http://www.w3.org/1999/xhtml">test</div>
  4. </foreignObject>
  5. </svg>

在您的示例中,<div>元素位于SVG名称空间中,即它是一个SVG元素,而不是HTML元素(尽管是非标准元素)。
<foreignObject>元素也有一个requiredExtensions属性来告诉浏览器使用哪个扩展,但是不同的浏览器似乎对这个属性的解释不同,所以最好不要设置它。

相关问题