org-chart
org-chart copied to clipboard
Examples => Safari Issue
Safari is unable to correctly render foreignObject elements, where content is overflowing.
And it leads to weird issues.
First two examples should be free of this issue, since I made sure that they would not overflow specified dimensions (so it should be safe to reuse in case safari support is important)
Hello @bumbeishvili , by when do you plan to fix the issues with Safari browser?
To be honest I hope safari will fix that issue. I don't plan to make changes to existing examples.
I made the first two examples of Safari safe. Meaning they will work in Safari, but other examples only work in Chrome/Edge/Firefox
Hey @bumbeishvili This package is awesome btw! Do you have any idea why it's not working in Safari (to build a workaround if necessary)? I find the expand/collapse button isn't working even on the first two examples. Seems like the click event is not being registerred.
@kmcgrady Main issue is that safari is unable to correctly render foreignObject
element, when content is overflowing.
In chrome overflow:visible
works well for foreignObject
and it's possible to create very customized node content because of that. But in Safari, it leads to weird issues.
I made sure that in the first two examples, content would not overflow to specified node dimensions and they were seemed to be working in safari well.
As for the click
event, this was not happening before. I've created a new Github issue and will probably fix it in the following days.
https://github.com/bumbeishvili/org-chart/issues/86
Update: - expand & collapse button should be working for the first two (safari proof) examples now
I had similar issue (https://github.com/bumbeishvili/org-chart/issues/120) with this. Tweaked the code a little and it worked for me. I hope it helps you too.
https://stackblitz.com/edit/web-platform-xkdtpd
@bumbeishvili thank you for such a great library!
I ran into two issues in Safari related due to overflowing content in the foreignobject
:
- Dragging issues : already highlighted
- Content that was using
position
was displayed to the top left of the page : already captured in this issue and https://github.com/bumbeishvili/org-chart/issues/120
I was able to get around those issues by making sure all content was in foreignobject
and now I'm wondering if it would be possible to extend the connecting lines with linkUpdate
?
Here are some screenshots to hopefully clarify:
Hoping to extend the line to touch the white background ⬆️
@jd-gray in your place I'd have three options:
-
Just move the node to the without going outside foreignObject element? Not sure if issues still appear after that. Also as I see you are using drop shadow. I have to warn you that it will have horrible performance hit
-
If I remember correctly, depending on your layout type, this line is responsible for line's x position (again, not sure, spent so much time debugging it )
https://github.com/bumbeishvili/org-chart/blob/3b7cc9d332d657ed5e710631abaad36dfc5ebe0f/src/d3-org-chart.js#L128
Unfortunately, you can't override this particular piece, you'll need to copy everything under layout bindings and then change only specific ones (If you are not using other layouts, you can drop some of them)
So, it will be like this roughly:
chart.layoutBindings(
{
"left": {
"nodeLeftX": node => 0,
"nodeRightX": node => node.width,
"nodeTopY": node => - node.height / 2,
"nodeBottomY": node => node.height / 2,
"nodeJoinX": node => node.x + node.width,
"nodeJoinY": node => node.y - node.height / 2,
"linkJoinX": node => node.x + node.width, // Modify this line, like node.x - I think this is the correct property, I'd try all link related properties
"linkJoinY": node => node.y,
"linkX": node => node.x,
"linkY": node => node.y,
"linkCompactXStart": node => node.x + node.width / 2,//node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
"linkCompactYStart": node => node.y + (node.compactEven ? node.height / 2 : -node.height / 2),
"compactLinkMidX": (node, state) => node.firstCompactNode.x,// node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"compactLinkMidY": (node, state) => node.firstCompactNode.y + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"linkParentX": node => node.parent.x + node.parent.width,
"linkParentY": node => node.parent.y,
"buttonX": node => node.width,
"buttonY": node => node.height / 2,
"centerTransform": ({ root, rootMargin, centerY, scale, centerX }) => `translate(${rootMargin},${centerY}) scale(${scale})`,
"compactDimension": {
sizeColumn: node => node.height,
sizeRow: node => node.width,
reverse: arr => arr.slice().reverse()
},
"nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
if (state.compact && node.flexCompactDim) {
const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
return result;
};
return [height + siblingsMargin, width + childrenMargin]
},
"zoomTransform": ({ centerY, scale }) => `translate(${0},${centerY}) scale(${scale})`,
"diagonal": this.hdiagonal.bind(this),
"swap": d => { const x = d.x; d.x = d.y; d.y = x; },
"nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x},${y - height / 2})`,
},
"top": {
"nodeLeftX": node => -node.width / 2,
"nodeRightX": node => node.width / 2,
"nodeTopY": node => 0,
"nodeBottomY": node => node.height,
"nodeJoinX": node => node.x - node.width / 2,
"nodeJoinY": node => node.y + node.height,
"linkJoinX": node => node.x,
"linkJoinY": node => node.y + node.height,
"linkCompactXStart": node => node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
"linkCompactYStart": node => node.y + node.height / 2,
"compactLinkMidX": (node, state) => node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"compactLinkMidY": node => node.firstCompactNode.y,
"compactDimension": {
sizeColumn: node => node.width,
sizeRow: node => node.height,
reverse: arr => arr,
},
"linkX": node => node.x,
"linkY": node => node.y,
"linkParentX": node => node.parent.x,
"linkParentY": node => node.parent.y + node.parent.height,
"buttonX": node => node.width / 2,
"buttonY": node => node.height,
"centerTransform": ({ root, rootMargin, centerY, scale, centerX }) => `translate(${centerX},${rootMargin}) scale(${scale})`,
"nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node, compactViewIndex }) => {
if (state.compact && node.flexCompactDim) {
const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
return result;
};
return [width + siblingsMargin, height + childrenMargin];
},
"zoomTransform": ({ centerX, scale }) => `translate(${centerX},0}) scale(${scale})`,
"diagonal": this.diagonal.bind(this),
"swap": d => { },
"nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width / 2},${y})`,
},
"bottom": {
"nodeLeftX": node => -node.width / 2,
"nodeRightX": node => node.width / 2,
"nodeTopY": node => -node.height,
"nodeBottomY": node => 0,
"nodeJoinX": node => node.x - node.width / 2,
"nodeJoinY": node => node.y - node.height - node.height,
"linkJoinX": node => node.x,
"linkJoinY": node => node.y - node.height,
"linkCompactXStart": node => node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
"linkCompactYStart": node => node.y - node.height / 2,
"compactLinkMidX": (node, state) => node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"compactLinkMidY": node => node.firstCompactNode.y,
"linkX": node => node.x,
"linkY": node => node.y,
"compactDimension": {
sizeColumn: node => node.width,
sizeRow: node => node.height,
reverse: arr => arr,
},
"linkParentX": node => node.parent.x,
"linkParentY": node => node.parent.y - node.parent.height,
"buttonX": node => node.width / 2,
"buttonY": node => 0,
"centerTransform": ({ root, rootMargin, centerY, scale, centerX, chartHeight }) => `translate(${centerX},${chartHeight - rootMargin}) scale(${scale})`,
"nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
if (state.compact && node.flexCompactDim) {
const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
return result;
};
return [width + siblingsMargin, height + childrenMargin]
},
"zoomTransform": ({ centerX, scale }) => `translate(${centerX},0}) scale(${scale})`,
"diagonal": this.diagonal.bind(this),
"swap": d => { d.y = -d.y; },
"nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width / 2},${y - height})`,
},
"right": {
"nodeLeftX": node => -node.width,
"nodeRightX": node => 0,
"nodeTopY": node => - node.height / 2,
"nodeBottomY": node => node.height / 2,
"nodeJoinX": node => node.x - node.width - node.width,
"nodeJoinY": node => node.y - node.height / 2,
"linkJoinX": node => node.x - node.width,
"linkJoinY": node => node.y,
"linkX": node => node.x,
"linkY": node => node.y,
"linkParentX": node => node.parent.x - node.parent.width,
"linkParentY": node => node.parent.y,
"buttonX": node => 0,
"buttonY": node => node.height / 2,
"linkCompactXStart": node => node.x - node.width / 2,//node.x + (node.compactEven ? node.width / 2 : -node.width / 2),
"linkCompactYStart": node => node.y + (node.compactEven ? node.height / 2 : -node.height / 2),
"compactLinkMidX": (node, state) => node.firstCompactNode.x,// node.firstCompactNode.x + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"compactLinkMidY": (node, state) => node.firstCompactNode.y + node.firstCompactNode.flexCompactDim[0] / 4 + state.compactMarginPair(node) / 4,
"centerTransform": ({ root, rootMargin, centerY, scale, centerX, chartWidth }) => `translate(${chartWidth - rootMargin},${centerY}) scale(${scale})`,
"nodeFlexSize": ({ height, width, siblingsMargin, childrenMargin, state, node }) => {
if (state.compact && node.flexCompactDim) {
const result = [node.flexCompactDim[0], node.flexCompactDim[1]]
return result;
};
return [height + siblingsMargin, width + childrenMargin]
},
"compactDimension": {
sizeColumn: node => node.height,
sizeRow: node => node.width,
reverse: arr => arr.slice().reverse()
},
"zoomTransform": ({ centerY, scale }) => `translate(${0},${centerY}) scale(${scale})`,
"diagonal": this.hdiagonal.bind(this),
"swap": d => { const x = d.x; d.x = -d.y; d.y = x; },
"nodeUpdateTransform": ({ x, y, width, height }) => `translate(${x - width},${y - height / 2})`,
},
}
})
- Just copy the code here and use it locally and modify as you want. It's just a one class
https://github.com/bumbeishvili/org-chart/blob/3b7cc9d332d657ed5e710631abaad36dfc5ebe0f/src/d3-org-chart.js
Thank you @bumbeishvili, I'll give those suggestions a try!
@bumbeishvili as a heads up I was able to fix my issue by updating a few properties and spread the properties instead of pasting the entire layoutBindings
. Again thank you for your suggestions!
const yOverride = 18;
const innerCardWidth = 104;
chart.layoutBindings({
...chart.layoutBindings(),
top: {
...chart.layoutBindings().top,
linkY: (node: HierarchyNodeD3) => node.y + yOverride,
compactLinkMidY: (node: HierarchyNodeD3) => node.firstCompactNode.y + yOverride,
linkCompactXStart: (node: HierarchyNodeD3) =>
node.x + (node.compactEven ? innerCardWidth / 2 : -innerCardWidth / 2),
buttonY: (node) => node.height - 10,
},
});
Good to know
Hi @bumbeishvili I'm getting this issue using shadows. Do you know how can I fix it?

I don't know right away, I guess it requires trial and errors. I won't recommend using shadows generally, it noticeably slows org chart down
hello @bumbeishvili and others! I'm trying to pdate my diagram to work also in Safari.
I read this thread and also the other related questions and I'm stuck as I'm not able to pinpoint which combination of styles is causing my diagram to break in Safari. I was hoping you could share some specific tips on how to troubleshoot the issues?
For example, any recommendation of display
and sizes?
hello again @bumbeishvili I found out that my issues are due to hard-coded sizes for the foreingObject and rect
for the button part. The code has a hard-coded 40x40 pixels.
Is there a way to change that, which is not changing the source code? I found a horrible hack to update the rect
and foreignObject
at every nodeUpdate
, but that can't be the right way.
chart.nodeUpdate((e, d) => {
maxY = Math.max(maxY, e.y, window.innerHeight);
const buttonNodes = document.getElementsByClassName('node-button-foreign-object');
for (const node of buttonNodes) {
node.setAttribute("width", 120)
node.setAttribute("x", -60)
}
const buttonRectNodes = document.getElementsByClassName('node-button-rect');
for (const node of buttonRectNodes)
node.setAttribute("width", 120)
});
Hi, this issue also bit me in another project, and I made some modifications in that part, but could not find a time to apply changes here as well.
Your solution can become a better hack of you try to use 'this', which refers to current node DOM element. Don't forget converting arrow function to the normal function
So, just replace the document with 'this' and it should work still.