Embedding / rendering problems loading a server side generated SVG asynchronously - javascript

I am trying to override some JavaScript code to optimize an existing solution of which I do not have access to the original source code. It is a Liferay based portal with some portlets that render an SVG image. The SVG is used as a graphical navigation element which can be used to drill down into a technical service model. The loading and refreshing of the SVG image can be slow because of the way it has been implemented. The SVG disappears for a couple of seconds because the refresh actually does 2 requests to the webserver. At first the complete object tag is regenerated server side and re-inserted in the dom.
The response of the first request looks like this:
<div><object type="image/svg+xml" data="generateSvg.svg” width="100" height="300" name="svgId"> <param name="wmode" value="transparent"></object></div>
The actual request of the SVG starts by the time the first response text is inserted into the DOM and the original object tag is removed. Because the SVG images can get quite complex and some general slowness of the software the SVG disappears and returns by the time it is received and rendered by the web-browser.
I would like to skip the first step and make sure the image does not “blink” by refreshing the SVG asynchronously. Using jQuery I am able to override the original JavaScript code and get the url of the SVG and do a jQuery ajax request. I am also able to insert the results in the DOM but when I insert it as an inline SVG the image is rendered different then when it is embedded in an object tag.
Some parts of svg content elements are cut off and this does not happen when the SVG is added as an object tag with an external url.
I either would like to embed the SVG in an object tag and load its url asynchronously or find a fix for the rendering problems of the inline SVG. I cannot change the SVG code since it is part of the existing software. I there a way to add the asynchronous loaded data to an object tag without adding the url to the data attribute?
My javascript for adding the SVG looks like this:
indentifier.loadSvg = (function(){
if(this.svgUrl){
$.ajax({
url: this.svgUrl,
//enclose the right div in which the svg should be loaded as a context
context: $('#_layout_WAR_Portlets_INSTANCE_' + this.portalId + '_plugin'),
dataType: 'xml'
}).done(function(svgDoc) {
//construct an svg node and add it to the DOM
var svgNode = $('svg', svgDoc);
var docNode = document.adoptNode(svgNode[0]);
$(docNode).attr('width', '100%').attr('height', '300');
this.html(docNode);
});
}
});

I'm not sure if it's possible inside an object tag but an Iframe should do the trick.
You could try creating a new Object tag with javascript, loading the svg and replacing the old object with the new one.
It would look something like this:
var datasrc = "#";
// Create new object tag
var newobj = jQuery('<obj>', {
id: 'newobject',
data: datasrc,
type: 'image/svg+xml'
});
// Set parameters
jQuery('<param />', {
name: 'src',
value: datasrc
}).appendTo('#newobj');
jQuery('<param />', {
name: 'wmode',
value: 'transparent'
}).appendTo('#newobj');
// Dynamically load content and replace when done
newobj.load(datasrc,function(){
$('#objectwrapper').empty();
newobj.appendTo('#objectwrapper');
alert("succes"); // Just to check if this works!
});
JSFiddle: http://jsfiddle.net/cfERU/9/
The alert wil fire twice, I believe this is a JSFiddle thing.

Related

IMG elements can be dragged from web pages to other applications. How can I implement this for dynamic SVG elements?

As stated in the question, you can drag an IMG element from a web page into any other application that accepts it. You can also right-click to select "Save Image As...".
Is there a way to make this work with images (which are dynamically generated)? I had some luck converting the SVGs to data urls and passing them to IMG tags, but this doesn't seem to work on all browsers and is cumbersome.
EDIT: the one answer lead me to consider using Blobs and URL.createObjectURL(). Not sure if this would be less brittle than data urls.
By saving the content in a separate file, and adding it into your webpage as the source of an img element:
<img src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/410.svg" />
EDIT: Converting an SVG to a data URL is probably the best way to go. Here are some of data URI's advantages/disadvantages, and here is the official caniuse page for data URIs. I'm not sure what you mean by "this doesn't seem to work on all browsers". Perhaps your encoded image (plus the encoding scheme text) is larger than 32kB (the maximum length of data URIs)?
You can leave the heavy lifting to a native JavaScript Web Component, supported in all modern browsers:
load the external SVG as text
post process SVG to encode for a DataURI
create <img>
set DataURI as src
!! replace the Web Component itself with the <img>
<style>
img { height:140px }
</style>
<svg-to-img src="//svg-cdn.github.io/heart.svg"></svg-to-img>
<svg-to-img src="//svg-cdn.github.io/joker-card.svg"></svg-to-img>
<svg-to-img src="//svg-cdn.github.io/svg_circle_spinner.svg"></svg-to-img>
<script>
customElements.define("svg-to-img", class extends HTMLElement {
async connectedCallback() {
let src = this.getAttribute("src");
let options = { /* fix potential CORS issues, client AND server side */ };
let svg = await (await fetch(src,options)).text();
let img = Object.assign(document.createElement("img"), {
src: "data:image/svg+xml," + svg.replace(/"/g, "'").replace(/#/g, '%23'),
onload: (e) => console.log("Loaded SVG as IMG", src),
onerror: (e) => console.error(e)
});
this.replaceWith(img);
}
})
</script>
Note: Where Browsers accept SVGs without NameSpace; for DataURIs xmlns="http://www.w3.org/2000/svg" is vital on your SVG!
So it looks like using a data url passed to an IMG element is the best way to accomplish my goal, and I ended up using the mini-svg-data-uri library to generate the urls from my (dynamically generated on client side) SVGs.
The real issue seemed to be that the library that generated the SVG for some reason inserted xmlns="" attributes on some browsers, no idea why. This wasn't a problem in any browser when the SVG was directly in the DOM (as an <svg> element), but fails when the problematic SVGs are used as data urls.
https://codesandbox.io/s/svg-in-different-forms-xpg6v0

OneNote JavaScript API access element

I have tried to create my first One Note Add In using the JavaScript API. I have tried the example in the MS documentaion (Build your first OneNote task pane add-in). This one works.
Now I want to try to change the formatting of an element in the document. For example I want to change the font colour of a text. However, I have not yet found a way to access the elements in a document.
Can I access elements in a document via a JS Add In to change their "style" property?
How can I do that?
Thanks
Micheal
Finally, I found a way to access the OneNote page content from the JS Add In. You can load the page content using
var page = context.application.getActivePage();
var pageContents = page.contents;
context.load(pageContents);
Now you have access to the page content in the qued commands.
return context.sync().then( function() {
var outline = pageContents.items[0].outline;
outline.appendHtml("<p>new paragraph</p>");
var p = outline.paragraphs;
context.load(p);
...
});
So consequently you can access element by element in document the hirarchy.

Parse a HTML String with JS without triggering any page loads?

As this answer indicates, a good way to parse HTML in JavaScript is to simply re-use the browser's HTML-parsing capabilities like so:
var el = document.createElement( 'html' );
el.innerHTML = "<html><head><title>titleTest</title></head><body><a href='test0'>test01</a><a href='test1'>test02</a><a href='test2'>test03</a></body></html>";
// process 'el' as desired
However, this triggers loading extra pages for certain HTML strings, for example:
var foo = document.createElement('div')
foo.innerHTML = '<img src="http://example.com/img.png">';
As soon as this example is run, the browser attempts to load the page:
How might I process HTML from JavaScript without this behavior?
I don't know if there is a perfect solution for this, but since this is merely for processing, you can before assigning innerHTMl replace all src attributes to be notSrc="xyz.com", this way it wont be loaded, and if you need them later in processing you can account for this.
The browser mainly will load images, scripts, and css files, this will fix the first 2, the css can be done by replacing the href attribute.
If you want to parse HTML response without loading any unnecessary resources like images or scripts inside, use DOMImplementation’s createHTMLDocument() to create new document which is not connected to the current one parsed by the browser and behaves as well as normal document.

Loading an SVG into the page using link rel="import"

I am trying to load an SVG file into a HTML page by first including the page in the HTML header:
<link rel="import" href="/views/Diagram.svg" />
then executing the following script:
var link = document.querySelector('link[rel="import"][href="/views/Diagram.svg"]');
var data = link.import;
document.getElementById("svg-diagram").innerHTML = data;
Even though the svg is valid (can be displayed), the imported data object contains a parseerror and the svg is not added to the page. I can add the svg using ajax, but since it is to be loaded multiple times, I'd prefer to pre-load it. How can I fix the code?
Instead of doing
document.getElementById("svg-diagram").innerHTML = data;
try
document.getElementById("svg-diagram").appendChild(data.documentElement);
The reason is because data.documentElement is an DOM node object while data is a document object.
Here's what is in the data object
Here is what we are going to make use of
appendChild was used instead of innerHTML because innerHTML expects a string while appendChild can make use of a node object

Replace part of the page with jQuery

I wrote a script and I want to change the language. I managed to do this using the .ajax() function in jQuery and fetching the new HTML.
Afterwards I want to replace the old HTML with the new HTML.
However, I do not want to exchange the whole HTML, but only part of it.
I know that the HTML returned includes a <div id="myDivId">...</div>, so I only want to get the content of that div from the HTML returned, and replace the content of my current div with the new content.
But, I can't seem to figure out how to fetch only that content from my new HTML. How can I do that? I tried using find, but no success.
$.ajax({
type: "GET",
url: "ajax.php",
data: "action=languagepack&subAction=box&newIso="+newIso+"&country="+country,
success: function(htmlCode){
var box = $(htmlCode).find('#myDivId').html();
alert(box);
}
});
Best regards,
Paul Peelen
Try jQuery.load()
This method is the simplest way to
fetch data from the server. It is
roughly equivalent to $.get(url, data,
success) except that it is a method
rather than global function and it has
an implicit callback function. When a
successful response is detected (i.e.
when textStatus is "success" or
"notmodified"), .load() sets the HTML
contents of the matched element to the
returned data. This means that most
uses of the method can be quite
simple:
$('#result').load('ajax/test.html');
Loading Page Fragments
The .load() method, unlike $.get(), allows us to specify a
portion of the remote document to be
inserted. This is achieved with a
special syntax for the url parameter.
If one or more space characters are
included in the string, the portion of
the string following the first space
is assumed to be a jQuery selector
that determines the content to be
loaded.
We could modify the example above to use only part of the document that
is fetched:
$('#result').load('ajax/test.html #container');
If you need HTML from only one container - then load is recommended way certainly. If you need some additional custom processing - are you sure that #myDivId inside blocks given from server and not one of these blocks? In the 2nd case you need filter inside of find (or wrap all HTML into some div to be sure that all elements will be inside it and won't be top level elements).
Shoot that through a php, run a explode change the contents, recreate the div, replace it where you want.
You can do all this with ajax too, but trigger the php file
try this with returned html:
var d = $("<div/>");
$(d).html(msg.d);
var box = $(d).find("#testDiv").html();
alert(box);

Categories

Resources