Html 除非纯文本,否则不显示 SVG 外来对象内容
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13848039/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
SVG foreignObject contents do not display unless plain text
提问by Atomix
I am trying to output HTML using the foreignObject tag inside an SVG drawing. I am using d3 to generate the elements. The only time the HTML content inside the foreignObject tag shows up is when the content inside the foreignObect tag is plain text, otherwise it just shows up as empty/blank. Please see this jsfiddle for an example of my problem: http://jsfiddle.net/R9e3Y/29/
我正在尝试使用 SVG 绘图中的 foreignObject 标签输出 HTML。我正在使用 d3 来生成元素。foreignObject 标签内的 HTML 内容唯一出现的时候是 foreignObect 标签内的内容是纯文本,否则它只会显示为空/空白。请参阅此 jsfiddle 以获取我的问题示例:http: //jsfiddle.net/R9e3Y/29/
The contents inside the foreignObject tag show up on inspecting the element this this:
在检查元素时,foreignObject 标签内的内容显示如下:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<foreignObject x="40" y="40" width="100" height="100">
<div>test</div>
</foreignObject>
</svg>
but are not visible on the screen? How do I get the content to show up?
但在屏幕上不可见?如何让内容显示出来?
回答by Robert Longson
Since you're using d3 you need to tell d3 that the div is a html div and not some element in the svg namespace. Try
由于您使用的是 d3,因此您需要告诉 d3 div 是一个 html div 而不是 svg 命名空间中的某个元素。尝试
.append("xhtml:div")
回答by Thomas W
<foreignObject>
allows embedding all kinds of markup, not just HTML. This means, there must be a way of determining what language is used. That's where namespaces come into play.
<foreignObject>
允许嵌入各种标记,而不仅仅是 HTML。这意味着,必须有一种方法来确定使用什么语言。这就是命名空间发挥作用的地方。
To tell SVG what kind of foreignObject
you have, you have to put the content in the proper namespace.
要告诉 SVGforeignObject
您拥有哪种类型,您必须将内容放在正确的命名空间中。
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<foreignObject x="40" y="40" width="100" height="100">
<div xmlns="http://www.w3.org/1999/xhtml">test</div>
</foreignObject>
</svg>
In your example, the <div>
element is in the SVG namespace, i.e. it's an SVG element, not an HTML one (albeit a non-standard one).
在您的示例中,该<div>
元素位于 SVG 命名空间中,即它是一个 SVG 元素,而不是 HTML 元素(尽管是非标准元素)。
The <foreignObject>
element also has a requiredExtensions
attribute to tell the browser which extension is used, however different browsers seem to be interpreting this attribute differently, so it's probably best to not set it.
该<foreignObject>
元素还有一个requiredExtensions
属性来告诉浏览器使用哪个扩展,但是不同的浏览器似乎对这个属性的解释不同,所以最好不要设置它。
回答by Melle
Make sure you set width
and height
parameters.
确保您设置width
和height
参数。
Adding xmlns
did not fix things for me. It turned out the problem for me was even more simple... I did not add width
and height
parameters, so content inside the foreignObject
did not show, even though it was there. Both parameters seem to default to 0.
添加xmlns
并没有为我解决问题。原来我的问题更简单...我没有添加width
和height
参数,所以里面的内容foreignObject
没有显示,即使它在那里。两个参数似乎都默认为 0。
回答by Cris
I developed something for this. The code is below. A more advanced version uses a proxy to pull in external non-CORS resources. svg foreignObject
blocks loading of any non-CORS cross-origin request. I wrote a simple proxy to run on Runkit. See at the bottom.
我为此开发了一些东西。代码如下。更高级的版本使用代理来拉入外部非 CORS 资源。svgforeignObject
阻止加载任何非 CORS 跨域请求。我写了一个简单的代理来在 Runkit 上运行。见底部。
The limitations of this are: no external non-CORS fonts, no non-CORS images. Anyone who wants to help improve this, including adding in support for images and fonts, can contribute here: https://github.com/dosyago-coder-0/dompeg.js/blob/master/dompeg.js
这样做的限制是:没有外部非 CORS 字体,没有非 CORS 图像。任何想要帮助改进这一点的人,包括添加对图像和字体的支持,都可以在这里做出贡献:https: //github.com/dosyago-coder-0/dompeg.js/blob/master/dompeg.js
web page script:
网页脚本:
(async function (){
const width = document.scrollingElement.scrollWidth;
const height = document.scrollingElement.scrollHeight;
const doc = document.implementation.createHTMLDocument('');
doc.write(document.documentElement.outerHTML);
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);
const styles = [];
for( let i = 0; i < document.styleSheets.length; i++ ) {
const ss = document.styleSheets[i];
if ( ss.cssRules ) {
for( let j = 0; j < ss.cssRules.length; j++ ) {
styles.push( ss.cssRules[j].cssText );
}
} else {
try {
const res = await fetch(ss.href);
const cssText = await res.text();
styles.push(cssText);
} catch(e) {
/** fetch to proxy here as fallback
* uncomment if you set up your proxy server
try {
const res = await fetch(`https://${YOUR PROXY SERVER}.runkit.sh/?url=${btoa(ss.href)}`);
const cssText = await res.text();
styles.push(cssText);
} catch(e) { **/
console.warn(`Exception adding styles from ${ss.href}`, e, e.stack);
/** uncomment if you setup proxy
}
**/
}
}
}
Array.from( doc.querySelectorAll('noscript, link, script')).forEach( el => el.remove() );
stripComments(doc);
Array.from( doc.querySelectorAll('*[style]')).forEach( el => {
const styleText = el.getAttribute('style');
const uniq = (Math.random()+''+performance.now()).replace(/\./g,'x');
const className = `class${uniq}`;
const cssText = `.${className} {${ styleText }}`;
styles.push( cssText );
el.classList.add( className );
});
const styleElement = doc.createElement('style');
styleElement.innerText = styles.join('\n');
doc.documentElement.appendChild(styleElement);
const canvas = document.createElement('canvas');
Object.assign( canvas, {width,height});
const ctx = canvas.getContext('2d');
const data = `
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
<foreignObject width="100%" height="100%">
${(new XMLSerializer).serializeToString(doc).slice(15)}
</foreignObject>
</svg>`;
const DOMURL = window.URL || window.webkitURL || window;
const img = new Image();
const svg = new Blob([data], {type: 'image/svg+xml'});
Object.assign( img, {width,height});
img.crossOrigin = "Anonymous";
img.onload = function() {
ctx.fillStyle = 'white';
ctx.fillRect( 0, 0, canvas.width, canvas.height );
ctx.drawImage(img, 0, 0);
const datauri = canvas.toDataURL('image/jpeg');
const anchor = document.createElement('a');
anchor.download = 'screen.jpg';
anchor.href = datauri;
anchor.target = "_new";
anchor.innerText = 'download screen.jpg';
anchor.addEventListener('click', e => {e.stopPropagation();anchor.remove();}, { capture: true });
document.body.appendChild(anchor);
Object.assign( anchor.style, {
position: 'fixed',
background:'white',
fontSize: '18px',
fontFamily: 'monospace',
color: 'blue',
top: 0,
left: 0,
zIndex: Number.MAX_SAFE_INTEGER
});
}
img.src = buildSvgImageUrl(data);
img.style.position = "absolute";
img.style.zIndex = "10000000";
img.style.backgroundColor = "white";
//document.body.appendChild(img);
function buildSvgImageUrl(svg) {
const b64 = btoa(unescape(encodeURIComponent(svg)));
return "data:image/svg+xml;base64," + b64;
}
function stripComments(docNode){
const commentWalker = docNode.evaluate('//comment()', docNode, null, XPathResult.ANY_TYPE, null);
let comment = commentWalker.iterateNext();
const cuts = [];
while (comment) {
cuts.push(comment);
comment = commentWalker.iterateNext();
}
cuts.forEach( node => node.remove());
}
}());
runkit proxy server script:
runkit 代理服务器脚本:
const request = require("request");
const rp = require('request-promise');
const {URL} = require('url');
const express = require("@runkit/runkit/express-endpoint/1.0.0");
const b64 = require('base-64');
const bodyParser = require('body-parser');
const page = (url,err) => `
<form method=POST style="
position: fixed;
position: sticky;
display: table;
top: 0px;
z-index:12000000;
background: white;">
<label for=hider99>X</label><input id=hider99 type=checkbox>
<style>
#hider99:checked ~ fieldset {
display: none;
}
</style>
<fieldset><legend>Proxy</legend>
<p>
<input required type=url size=62 name=url placeholder="any url" value="${url||'https://google.com/favicon.ico'}">
<button style=background:lime>Load</button>
${ !! err ? `<p><span style=color:red>${err}</span>` : '' }
</fieldset>
</form>`;
const app = express(module.exports);
app.use(bodyParser.urlencoded({ extended: false }));
app.get("/", async (req,res,next) => {
console.log(req.query.url);
let url;
res.type('html');
res.set('access-control-allow-origin', '*');
try {
url = b64.decode(req.query.url);
new URL(url);
} catch(e) { res.end(page('',"not a url"+e)); return; }
try {
res.type(type(url));
const data = await rp(url);
res.end(data);
} catch(e) { res.end(page('',""+e)); }
});
app.get("/:anything", async (req,res,next) => {
res.type('html');
res.end('404 Not found');
});
function type(s = '') {
return s.split(/\./g).pop() || 'html';
}
void 0;
回答by will yang
var a = function(a) {
var b = enter code heredocument.createElementNS("http://www.w3.org/1999/xhtml", "div");
b.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg">' + a + "</svg>";
for (var c = document.createDocumentFragment(); b.firstChild.firstChild; ){
c.appendChild(b.firstChild.firstChild);
}
return c;
}
$('#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>'));