I have iframe-resizer working well, except I'm having trouble determining the viewport width/height from within the iframe content. I found I could call parentIFrame.getPageInfo(callback), but the object I get back looks like this:
{
clientHeight: 6009,
clientWidth: 1680,
iframeHeight: 5968,
iframeWidth: 1230,
offsetLeft: 0,
offsetTop: 40,
scrollLeft: 0,
scrollTop: 580
}
I expected clientWidth and clientHeight to be the window viewport width/height. clientWidth looks correct, but clientHeight just looks like iframeHeight + offsetTop + 1px, whereas my browser window has a height of around 1000px. Am I misunderstanding what clientWidth/clientHeight mean or am I doing something wrong?
I found the problem was that the host page didn't have a DOCTYPE set at the top of its page. Once <!DOCTYPE html> was added to the top of the page, it worked!
Related
I am trying to make a screenshot at the bottom of the long page (e.g. http://www.taoism.net/ttc/complete.htm) like so:
Nightmare({ show: false })
.viewport(1024, 30000)
.goto('http://www.taoism.net/ttc/complete.htm')
.wait()
.screenshot(sImagePath, {
x : 0,
y : 27711,
width : 1024,
height : 133
});
Screenshot file size is 0 bytes.
Tested with different y values, it works until ~8000px.
Tried using .scrollTo, it did not help.
Does anybody know a workaround?
P.S. Nightmarejs wraps around electron browser.
This might be what you've looking for https://github.com/segmentio/nightmare/issues/328#issuecomment-159311982
Basically you dynamically adjust the viewport dimensions based on the content to grab size.
I have a webpage with an SVG. On some of its shapes I need to display a tooltip. However, I can't get the tooltip to appear where it should, just some pixels away from the shape itself.
It appears way on the right hand side of the screen, maybe some 300px away.
The code I am using to get the coordinates is as follows:
d3.select("body")
.select("svg")
.select("g")
.selectAll("circle")
.on("mouseover", function(){return tooltip.style("visibility", "visible");})
.on("mousemove", function(){
var svgPos = $('svg').offset(),
/*** Tooltip ***/
//This should be the correct one, but is displaying at all working at all.
/*x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;*/
//This displays a tool tip but way much to the left of the screen.
x = svgPos.left + d3.event.target.cx.animVal.value,
y = svgPos.top + d3.event.target.cy.animVal.value;
Tooltip
window.alert("svgPos: "+svgPos+" top: "+y+"px left: "+x+"px "+d3.event.target.cx.animVal.value);
return tooltip.style("top", x+"px").style("left",y+"px");
})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
I got to this code following this SO post.
I have changed $(ev.target).attr(cx) as it is not returning a value on my machine; d3.event.target.cx is, even though it seems it is not affecting the end result anyway.
What am I doing wrong? Could somebody help me please? Thank you very much in advance for your time.
If your tooltip is an HTML element, then you want to position it relative to the page as a whole, not the internal SVG coordinates, so accessing the cx/cy value is just complicating things. I can't say for sure without looking at your code, but if you have any transforms on your <svg> or <g> elements, then that could be what's throwing you off.
However, there is a much easier solution. Just access the mouse event's default .pageX and .pageY properties, which give the position of the mouse relative to the HTML body, and use these coordinates to position your tooltip div.
Example here: http://fiddle.jshell.net/tPv46/1/
Key code:
.on("mousemove", function () {
//console.log(d3.event);
return tooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Even with rotational transforms on the SVG circles, the mouse knows where it is on the page and the tooltip is positioned accordingly.
There are other ways to do this, including getting a tooltip to show up in a fixed location relative to the circle instead of following the mouse around, but I just checked the examples I was working on and realized they aren't cross-browser compatible, so I'll have to standardize them and get back to you. In the meantime, I hope this gets you back on track with your project.
Edit 1
For comparison, here is the same example implemented with both an HTML tooltip (a <div> element) and an SVG tooltip (a <g> element).
http://fiddle.jshell.net/tPv46/4/
The default mouse event coordinates may be great for positioning HTML elements that are direct children of <body>, but they are less useful for positioning SVG elements. The d3.mouse() function calculates the mouse coordinates of the current event relative to a specified SVG element's coordinate system, after all transformations have been applied. It can therefore be used to get the mouse coordinates in the form we need to position an SVG tooltip.
Key code:
.on("mousemove", function () {
var mouseCoords = d3.mouse(
SVGtooltip[0][0].parentNode);
//the d3.mouse() function calculates the mouse
//position relative to an SVG Element, in that
//element's coordinate system
//(after transform or viewBox attributes).
//Because we're using the coordinates to position
//the SVG tooltip, we want the coordinates to be
//with respect to that element's parent.
//SVGtooltip[0][0] accesses the (first and only)
//selected element from the saved d3 selection object.
SVGtooltip
.attr("transform", "translate(" + (mouseCoords[0]-30)
+ "," + (mouseCoords[1]-30) + ")");
HTMLtooltip
.style("top", (d3.event.pageY + 16) + "px")
.style("left", (d3.event.pageX + 16) + "px");
})
Note that it works even though I've scaled the SVG with a viewBox attribute and put the tooltip inside a <g> with a transform attribute.
Tested and works in Chrome, Firefox, and Opera (reasonably recent versions) -- although the text in the SVG tooltip might extend past its rectangle depending on your font settings. One reason to use an HTML tooltip! Another reason is that it doesn't get cut off by the edge of the SVG.
Leave a comment if you have any bugs in Safari or IE9/10/11. (IE8 and under are out of luck, since they don't do SVG).
Edit 2
So what about your original idea, to position the tooltip on the circle itself? There are definite benefits to being able to position the tip exactly: better layout control, and the text doesn't wiggle around with the mouse. And most importantly, you can just position it once, on the mouseover event, instead of reacting to every mousemove event.
But to do this, you can no longer just use the mouse position to figure out where to put the tooltip -- you need to figure out the position of the element, which means you have to deal with transformations. The SVG spec introduces a set of interfaces for locating SVG elements relative to other parts of the DOM.
For converting between two SVG transformation systems you use SVGElement.getTransformToElement(SVGElement); for converting between an SVG coordinate system and the screen, you use SVGElement.getScreenCTM(). The result are transformation matrices from which you can
extract the net horizontal and vertical translation.
The key code for the SVG tooltip is
var tooltipParent = SVGtooltip[0][0].parentNode;
var matrix =
this.getTransformToElement(tooltipParent)
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
SVGtooltip
.attr("transform", "translate(" + (matrix.e)
+ "," + (matrix.f - 30) + ")");
The key code for the HTML tooltip is
var matrix = this.getScreenCTM()
.translate(+this.getAttribute("cx"),
+this.getAttribute("cy"));
absoluteHTMLtooltip
.style("left",
(window.pageXOffset + matrix.e) + "px")
.style("top",
(window.pageYOffset + matrix.f + 30) + "px");
Live example: http://fiddle.jshell.net/tPv46/89/
Again, I'd appreciate a confirmation comment from anyone who can test this in Safari or IE -- or any mobile browser. I'm pretty sure I've used standard API for everything, but just because the API is standard doesn't mean it's universally implemented!
I am trying to place an SVG Text-element according to the width and height of the text by getting the bounding box using the getBBox() method.
If the text is using a websafe font, this works reasonably well across different browsers, but if the text is styled using #font-face and a custom webfont, then the width of the text is returned incorrectly in Firefox (Mac) and Safari (iOS).
It works perfectly in both Safari (Mac) and Chrome (Mac).
If the gray box has the same width as the text, then it works in that browser.
Does anybody have an idea on how to get the correct width of the text bounding box in all browsers?
The browser is calculating the bounding box before it has finished loading/applying #font-face, assuming you don't need IE, you can wrap your BBox calculation function inside a document.fonts.ready promise...
document.fonts.ready.then(() => const bbox = textEl.getBBox());
Here is an example at work that exhibits the problem and the fix:
const xmlns = "http://www.w3.org/2000/svg";
const correct = document.getElementById("correct");
const incorrect = document.getElementById("incorrect");
visualizeBBox(incorrect);
document.fonts.ready.then(()=> visualizeBBox(correct));
function visualizeBBox(el){
const bbox = el.getBBox();
const rect = document.createElementNS(xmlns, "rect");
for (prop in bbox) rect.setAttribute(prop, bbox[prop]);
document.querySelector("svg").appendChild(rect);
}
svg text {
font-family: 'Diplomata SC', serif;
}
svg rect {
stroke: red;
fill: none;
}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Diplomata+SC&display=swap" rel="stylesheet">
<svg xmlns="https://www.w3.org/2000/svg" width="600" height="400">
<text x="0" y="40" font-size="24" id="correct">Correct dimensions</text>
<text y="100" font-size="24" id="incorrect">Incorrect dimensions</text>
<svg>
Today I ran into similar issue. Duopixel is right that getBBox() might return momental metric which may be unexpected because external font hasn't been loaded yet and some standard font is used instead.
The problem in WebKit (tested in Chrome 24.0.1312.52 and 26.0.1389.0 canary) is that the browser defers external font loading until it is first effectively used anywhere on the page. So even if you wait for onreadystatechange to become "complete" you are not guaranteed to have font metrics ready when calling getBBox() - you may still be the first one rendering a text styled with external font, inserting it into the document and immediately calling getBBox() on it (my case).
My workaround instead of calling mySVGInitCode() directly I do:
$("body").append(
$("<div/>")
.attr("class", "force-external-font-loading")
.attr("style", "font-family: \"xkcd\";visibility:hidden;position:absolute")
.text("x")
);
setTimeout(function(){ mySVGInitCode() }, 100); // 100ms is just arbitrary waiting time which should be sufficient for fetching the external font on a fast network, anyone has a better solution?
As you can see I dynamically insert absolutely positioned styled piece of text to force external font loading (visibility:hidden is important here instead of display:none). Then I wait some time before I execute my SVG code which could potentially render something and then immediately ask for metrics.
I have a huge svg 3200*1800. I only want to show a part of that image something like 400*1000, ensuring that the width is the dominant attribute and having a scroll bar for the height but when I set viewbox it increase the width to display the added height.
viewBox="900 550 400 1000"
Is their a way to stop this happening?
I worked it out you need to increase the height relative to the viewbox for example I ended up with something like this:
width="1400"
height="4000"
viewBox="966 555 350 1000"
Compared to what I used to have:
width="350"
height="1000"
viewBox="966 555 350 1000"
You just set 'preserveAspectRatio' to "none" along with your 'viewBox' attribute, then your problem is solved.
This answer builds on Shane's answer (which does not cater to variable window sizes)...
To have width-dominant overflows:
Let the 'viewbox' define the portion of the graphic to display (any known aspect ratio)
Let the svg element have default width and height (100%)
With javascript, dynamically set the height of the svg element every time the window resizes
The code below works for my learning project and is NOT production code.
In the head element:
<script type="application/javascript">
var svgRatio = ${viewboxRatio}; // ratio must be known
// From http://stackoverflow.com/a/13651455
if(window.attachEvent) {
window.attachEvent('onresize', resizeSvg);
}
else if(window.addEventListener) {
window.addEventListener('resize', resizeSvg, true);
}
else {
//The browser does not support Javascript event binding
}
function resizeSvg() {
var height = window.innerWidth * svgRatio;
var svg = document.getElementsByTagName('svg')[0];
svg.setAttribute("height", height.toString());
}
</script>
At the end of the body:
<script type="application/javascript">
resizeSvg();
</script>
I'm using DOJO's ContentPane module. I have a div element in one of the panes and I need to give it a certain height - 100 pixels less than the height of the ContentPane so that the div changes its height dynamically when you change the ContentPane size by dragging the splitters. I'm new to Dojo and would be happy if somebody could help me with this.
Thanks.
I think the best solution is via nested BorderContainers with properly set splitters, because that way dijit/layout will take care of resizing and you won't need to write any JavaScript code and your layout will be based solely on CSS.
It's kinda cumbersome to explain, so I created a working example for you at jsFiddle: http://jsfiddle.net/phusick/Ayg8F/ + a diagram:
NB: Do not forget to set height: 100% for html, body and the top BorderContainer.
The drawback of this solution is you will have to replace plain divs with ContentPanes. If you do not want to or can't you can use dojo/aspect and connect to BorderContainer or ContentPane resize method and resize your divs manually whenever the size changes:
require([
"dojo/ready",
"dojo/aspect",
"dijit/registry",
"dijit/layout/ContentPane",
"dijit/layout/BorderContainer"
], function(
ready,
aspect,
registry
) {
ready(function() {
var bc = registry.byId("borderContainer1");
aspect.after(bc, "resize", function() {
// calculate and set <div> size here
console.log("resize divs");
});
});
});