Reload DOM after changes - javascript

I have a small problem with SVG in an embed tag. I update the source of an embed tag with an SVG file path with javascript. Then I have to update the viewbox attribute to resize correctly the SVG.
The problem is that the SVG tag is not available because the javascript execution is too fast.
An example :
//Creation and insertion by JQuery
var EmbedTag = $("<embed id='zoomSVG' src=idSVG + ".svg' type='image/svg+xml' width='500px' height='500px' />").appendTo(zoomGalleryHisto);
//This doesn't work : svgDoc is null
//SVG document recovery
var svgDoc = document.getElementById('zoomSVG').getSVGDocument();
This work :
setTimeout(function(){
//SVG document recovery
var svgDoc = document.getElementById('zoomSVG').getSVGDocument();
},100);
I would like to not use the setTimeout function because the timeout value depend on hardware. Sometimes 100 ms works fine but I have to find an universal solution.
After embed tag insertion, could I reload the DOM by javascript ? Is an existing event for embed tag when the load of the embedded object is done ?
Thanks for your help

Use the »onload« event of the tag to delay your function until it is loaded, than the should be available.

Call the code from the onload of the embed e.g.
var EmbedTag = $("<embed id='zoomSVG' onload='init()' src=idSVG + ".svg' type='image/svg+xml' width='500px' height='500px' />").appendTo(zoomGalleryHisto);
and then implement your code in init (or whatever you want to call it)

First, you create the element and then you trying to find it again in the DOM.
Second, javascript cant block until something happen, use callbacks insteed.
Something like that:
var svgDoc = false;
// Create an element
var EmbedTag = $("<embed id='zoomSVG' type='image/svg+xml' width='500px' height='500px' />");
// Append it to the dom
EmbedTag.appendTo(zoomGalleryHisto);
// Load the svg file
$.get('file.svg', function() {
// And then add it to the created element
EmbedTag.attr('src', 'file.svg');
svgDoc = EmbedTag.getSVGDocument();
});

Related

HTML object is blocking eventlistener

I have imported a svg as an object in HTML:
<object data="mySVG.svg" type="image/svg+xml" id="circle">
<img src="mySVG.svg" />
</object>
and I am trying to set an eventlistener on the whole page:
window.addEventListener('click', function(){
alert('Hello')
})
The problem is that the object blocks the eventlistener and when the user clicks on the image the alert is not fired. But when the user clicks anywhere else or over other elements, the alert is fired. How can I make it so the object is acting as the other elements and doesn't block the eventlistener?
I tried wait after the object is beaing loaded and then set the eventlistener but it didn't work.
If I import the SVG directly into HTML with svg tag it works, but the svg is quit big and it makes the HTML code really messy. I can't use the img tag either becuase I am also interacting with parts of the SVG with JS later.
As it can be seen in this codepen I've made: https://codepen.io/Dimertuper/pen/rNJoLrK (When you click outside the image it triggers, inside the image it doesn't)
Your <object> acts like an <iframe>, just like we wouldn't want any website to be able to embed our bank website in an iframe and see where we clicked, the <object> has the same "protection".
Even if the page are same-origin and can talk to each other, by default they won't receive any events from the other one.
But anyway what you probably want is to make the SVG document react to these events. For this, add the event listeners on that document directly.
// Wait for the <object> to be loaded
window.addEventListener("load", (evt) => {
const objEl = document.querySelector("object");
const svgDoc = objEl.getSVGDocument();
// Now you have access to the SVG document
// you can add event listeners to it as you wish
svgDoc.addEventListener("click", (evt) => {
console.log("clicked on", evt.target.outerHTML);
});
});
Unfortunately StackSnippets's null-origined iframes won't allow us to make live demos, so here is one on JSFiddle.
But beware the <object> element isn't gathering much love from implementers and spec authors these days and it may get removed from the standards at some point in the future.
So instead, you may prefer to actually use an <iframe> directly. Moreover since here we would access the loaded document, we can do the one thing that <object> can do and <iframe> can't: auto-resizing to the image content.
For this, when we get our SVG document, we grab its documentElement's BBox and set our <iframe>'s width and height attributes to the BBox's ones.
// Wait for the <iframe> to be loaded
window.addEventListener("load", (evt) => {
const frameEl = document.querySelector("iframe");
const svgDoc = frameEl.getSVGDocument();
// Resize the iframe to its content's size
const bbox = svgDoc.documentElement.getBBox();
frameEl.width = bbox.width;
frameEl.height = bbox.height;
svgDoc.addEventListener("click", (evt) => {
console.log("clicked on", evt.target.outerHTML);
});
});
Once again as a JSFiddle.
Per OP's requirements -
Needs to be able to click on window/document and receive the alert message even when clicking on the HTML object tag.
We can do this by removing the object tag as a clickable element with CSS pointer-events: none;.
object {
pointer-events: none;
}
https://codepen.io/LTFoReal/pen/NWyerZg?editors=1111
This link has work around. Using a transparent div to cover object image, or directly use svg image instead.
I checked the specification of object element. It's for embeded external content usage. So it has ability to load a full document, your case is load as image. The available property to do event binding for this element is contentDocument or getSvgDocument(). Both are null under your case, as it's loaded as svg image.
document.getElementsByTagName("object")[0].contentDocument
Check this link for detail. Hope this helps you.

Featherlight, JavaScript source for dynamic content

I'm evaluating Featherlight lightbox and I'm not able to implement code that satisfies my use case. I need a lightbox that will be used as a report viewer which displays dynamically created content assigned to a JavaScript variable. The value of the string is a valid HMTL5 page.
I've looked at the iframe example, but it depends upon a static iframe being in the DOM. That's not what I need.
I've reviewed this GitHub issue and this jsfiddle and I'm not able to successfully modify the fiddle to display a string.
This is an example of the string I would like to display:
var s = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Title of the document</title></head><body><p>Content of the document......</p></body></html>';
Is this possible and if so how?
I expect that $.featherlight() will be called manually in response to a button click.
The solution I came up with was to modify the Featherlight source code in 2 places as indicated in this block of code (currently around line 383).
iframe: {
process: function(url) {
var deferred = new $.Deferred();
var $content = $('<iframe/>')
.hide()
.attr('src', url)
.attr('id', this.namespace + '-id') // [KT] 10/31/2016
.css(structure(this, 'iframe'))
.on('load', function() {if ($content.show()) {deferred.resolve($content.show()) } else {deferred.resolve($content)} ; }) // [KT] 10/31/2016
// We can't move an <iframe> and avoid reloading it,
// so let's put it in place ourselves right now:
.appendTo(this.$instance.find('.' + this.namespace + '-content'));
return deferred.promise();
}
},
The id attribute is added to the iframe so content can be added by JavaScript, like this:
var s = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Title of the document</title></head><body><p>Content of the document......</p></body></html>';
var oIframe = document.getElementById('featherlight-id'); // Featherlight's iframe
var iframeDoc = (oIframe.contentDocument || oIframe.contentWindow.document);
iframeDoc.open();
iframeDoc.write(s);
iframeDoc.close();
This then works:
$.featherlight({iframe: 'about:blank', iframeWidth: '96%' });
The 2nd modification is required so that the url 'about:blank' doesn't raise an error.
I also modified the css so as to get the scroll bars to work as needed.
Edit: the issue with Featherlight not opening an iframe when the url is abount:blank has been fixed as of version 1.5.1.
Edit 2: Using v1.5.1, this works without having to make a modification to Featherlight to add an id to to the iframe:
var s = '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Title of the document</title></head><body><p>Content of the document......</p></body></html>';
$.featherlight({iframe: 'about:blank'});
var $iframe = $('.featherlight iframe');
$iframe.ready(function () {
$iframe.contents().find("body").append(s);
});
The accepted SO answer was used for this solution.

how to initialize variable in javascript with HTML code directly

How can I initialize a variable in JavaScript with a direct HTML code?
I am trying to put an animate tag in a variable so that I can append to the svg whenever a mouseover event occurs. I am using snap.svg.
var g = HTML CODE
var s = Snap("#svg");
s.mouseover(function(){
s.append(g);
});
Something like that. I know this syntax is wrong. I didn't want to write this tag in my html code but keep it hidden and then append it
Probably the easiest way:
var someHTML = '<div>Heeeyy</div>',
someElem = document.getElementById('someElement');
someElem.onmouseover = function(){
someElem.insertAdjacentHTML('beforeend', someHTML );
someElem.onmouseover = null; // Remove this if you want it to happen on every mouseover event
}
Alternatively, you can create a new element with document.createElement('DIV') and set the innerHTML to the desired html. Then append the created element with appendChild on the parent (desired element).
animateTransform doesn't have good support on some browsers like IE, so I tend to stay away from using that method, if something like Snap is available. If it is, I would do something similar to the following...
s = Snap(400, 620);
var c = s.circle(10,10,10);
var myMarkup = '<g><rect x="20" y="20" width="100" height="100"></g>'
c.mouseover( function() {
s.append( Snap.parse( myMarkup ) )
s.select('g').animate({ transform: 't50,50' }, 3000)
})
jsfiddle (hover over circle)
If you were just doing the markup to add an animateTransform element, you can skip the parsing/appending stuff as well, as thats not needed.

SVG in object problems with click event inside JS

i have an svg image inside object element of HTML document.
<object id="svg1" data="nejc/bg.svg" type="image/svg+xml">
Your browser doesn't support SVG
</object>
And i have javascript code. Inside of it i added click event which execute when svg is done loading. Code look for id in .svg file which i added on groups of elements. Everything is working. But i am having problem now and then when i load page the svg elements cant be clicked, but if i refresh it then the click is working just fine.
$( document ).ready(function() {
//button where are you
var a = document.getElementById("svg1");
//it's important to add an load event listener to the object, as it will load the svg doc asynchronously
a.addEventListener("load",function(){
var svgDoc = a.contentDocument; //get the inner DOM of svg
var delta = svgDoc.getElementById("right"); //get the inner element by id
delta.addEventListener("click",function(){
this.style.fill = '#DC7827';
setTimeout("document.location.href = '_mobile_whereareyou.php';",200);
},false); //add behaviour
},false);
//button place ID
var b = document.getElementById("svg1");
//it's important to add an load event listener to the object, as it will load the svg doc asynchronously
b.addEventListener("load",function(){
var svgDocB = b.contentDocument; //get the inner DOM of svg
var beta = svgDocB.getElementById("left"); //get the inner element by id
beta.addEventListener("click",function(){
this.style.fill = '#DC7827';
setTimeout("document.location.href = '_mobile_placeID.php';",200);
},false); //add behaviour
},false);
});
I am guessing that when svg elements could not be clicked then the svg image did not load correctly. Is there any way how to make sure that svg element is loaded and click is working ?
I think I came to solution. I was thinking right that svg did not load properly when click was not working. So i wrape all code in below block.
$('#svg1').load('nejc/bg.svg', null, function() {
//here is now my code given in question
});
This function load svg and on complete executes code in funtion.

How can I scroll content of <webview> tag from JavaScript?

atom-shell: https://github.com/atom/atom-shell
version: v0.20.2
I am using <webview> tag to embed a page. <webview> tag has shadow-root which has one tag, <object id="browser-plugin-1 ...>. So I tried to set scrollTop value for this tag like this.
var webView = document.getElementById('webview tag id');
var elm = webView.shadowRoot.firstChild; // elm is object tag
console.log(elm.scrollTop); // 0
elm.scrollTop = 100;
console.log(elm.scrollTop); // 0
But nothing happend...
Is it possible to control <webview> tag scroll position from outside?
Yes, do this instead:
var webView = document.getElementById('webview tag id');
webView.executeJavaScript("document.querySelector('body:first-child').scrollTop=100");
It's possible to execute any kind of javascript via the WebView.executeJavaScript(code) function which will evaluate the code inside the WebView.
To access your element you would first have to wait for the WebView to load, and then execute the javascript.
var webView = document.getElementById('webView');
webView.addEventListener('did-finish-load', scrollElement );
function scrollElement(){
var code = "var elm = document.querySelector('body:first-child'); elm.scrollTop = 100;";
webView.executeJavaScript(code);
}
Note: Haven't tested this code, it may have syntax errors.
Source (AtomShell's WebView tag documentation)

Categories