I have a notebook where things work when I use standard Observable ( thing = {} ) and yield my SVG nodes before calling a function that needs them to do getBoundingClientRect on the results.
thing = {
const svgNode = svg'<svg width=${layout.width} height=500 id='comms'></svg>';
// Do SVG stuff
yield svgNode;
renderStuffThatNeeds_getBoundingClientRect();
}
I prefer calling the cell as a function because then I get a named line in Obs that I can find when that results of that cell are rendered in another cell via ${thing()} so I tried making it a functionâŚ
If I try to turn the cell into a function ( function thing() {} ) I get the âyield is a reserved wordâ error because the cell isnât a generator.
If I try to turn the cell into a generator ( function* thing() {} ) the function after the yield fails with âTypeError: Cannot read properties of null (reading âgetBoundingClientRectâ)â
How is an Observable cell as a generator different than explicitly making a cell a function* ?
The difference is that when a cell yields (or returns) a value, the Observable inspector will insert that element into the DOM for you. Whereas if you have a local variable (such as the result of calling a generator function), it wonât be inserted into the DOM unless you do it yourself. So thisâŚ
{
for (let i = 0; i < 10; ++i) {
yield htl.html`hello ${i}`;
}
}
Is the same as this:
function* hellos() {
for (let i = 0; i < 10; ++i) {
yield htl.html`hello ${i}`;
}
}
{
for (const hello of hellos()) {
yield hello;
}
}
Is the same as thisâŚ
yield* hellos()
Is the same as thisâŚ
hellos()
If you want to use getBoundingClientRect, you must ensure that the DOM element has been added to the DOM first. You can do this implicitly by requiring that it has been yielded or returned as a cell value, or you can do it explicitly by adding it to the document.body (and then perhaps removing it after calling getBoundingClientRect).
Or, if you can avoid using getBoundingClientRect, then you donât have to worry about whether the DOM element is connected or not.