Created
February 1, 2020 08:15
-
-
Save thomasballinger/2c9718defa7c89daae163b6470a81f8b to your computer and use it in GitHub Desktop.
sketch of noticing that a cell has been removed or reexecuted. Useful for stopping timers, removing event listeners, etc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"application/javascript": [ | |
"\n", | |
"// Find deepest DOM ancestor that does not change on reexecute\n", | |
"const findParentOutputDiv = (el) => {\n", | |
" let candidate = el;\n", | |
" while (candidate) {\n", | |
" candidate = candidate.parentElement\n", | |
" if (candidate.className === 'output') {\n", | |
" return candidate;\n", | |
" }\n", | |
" }\n", | |
" throw Error(\"parent output div not found\");\n", | |
"}\n", | |
"\n", | |
"// Find shallowest DOM ancestor that is removed on delete\n", | |
"const findParentCell = (el) => {\n", | |
" let candidate = el;\n", | |
" while (candidate) {\n", | |
" candidate = candidate.parentElement\n", | |
" if (candidate.classList.contains('cell')) {\n", | |
" return candidate;\n", | |
" }\n", | |
" }\n", | |
" throw Error(\"cell not found not found\");\n", | |
" }\n", | |
"\n", | |
"const onCellReexecuteOrDelete = (element, cb) => {\n", | |
" \n", | |
" const outputDiv = findParentOutputDiv(element);\n", | |
" const cellDiv = findParentCell(element);\n", | |
" const notebookContainer = cellDiv.parentElement;\n", | |
" let onEither = () => { throw Error('callback not initialized yet'); }\n", | |
"\n", | |
" const reexecuteDetector = function(mutationsList, observer) {\n", | |
" for(let mutation of mutationsList) {\n", | |
" if (mutation.type === 'childList') {\n", | |
" onEither('cell reexecute');\n", | |
" return;\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
" \n", | |
" const cellDeleteDetector = function(mutationsList, observer) {\n", | |
" for (let mutation of mutationsList) {\n", | |
" if (mutation.type === 'childList') {\n", | |
" for (let node of mutation.removedNodes) {\n", | |
" if (node === cellDiv) {\n", | |
" onEither('cell delete')\n", | |
" return;\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"\n", | |
" const outputMutationObserver = new MutationObserver(reexecuteDetector);\n", | |
" const notebookMutationObserver = new MutationObserver(cellDeleteDetector);\n", | |
" \n", | |
" onEither = (reason) => {\n", | |
" outputMutationObserver.disconnect();\n", | |
" notebookMutationObserver.disconnect();\n", | |
" cb(reason);\n", | |
" }\n", | |
" \n", | |
" outputMutationObserver.observe(outputDiv, { childList: true });\n", | |
" notebookMutationObserver.observe(notebookContainer, { childList: true });\n", | |
"}\n", | |
"\n", | |
"window.onCellReexecuteOrDelete = onCellReexecuteOrDelete;\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.Javascript object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"%%javascript\n", | |
"\n", | |
"// Find deepest DOM ancestor that does not change on reexecute\n", | |
"const findParentOutputDiv = (el) => {\n", | |
" let candidate = el;\n", | |
" while (candidate) {\n", | |
" candidate = candidate.parentElement\n", | |
" if (candidate.className === 'output') {\n", | |
" return candidate;\n", | |
" }\n", | |
" }\n", | |
" throw Error(\"parent output div not found\");\n", | |
"}\n", | |
"\n", | |
"// Find shallowest DOM ancestor that is removed on delete\n", | |
"const findParentCell = (el) => {\n", | |
" let candidate = el;\n", | |
" while (candidate) {\n", | |
" candidate = candidate.parentElement\n", | |
" if (candidate.classList.contains('cell')) {\n", | |
" return candidate;\n", | |
" }\n", | |
" }\n", | |
" throw Error(\"cell not found not found\");\n", | |
" }\n", | |
"\n", | |
"const onCellReexecuteOrDelete = (element, cb) => {\n", | |
" \n", | |
" const outputDiv = findParentOutputDiv(element);\n", | |
" const cellDiv = findParentCell(element);\n", | |
" const notebookContainer = cellDiv.parentElement;\n", | |
" let onEither = () => { throw Error('callback not initialized yet'); }\n", | |
"\n", | |
" const reexecuteDetector = function(mutationsList, observer) {\n", | |
" for(let mutation of mutationsList) {\n", | |
" if (mutation.type === 'childList') {\n", | |
" onEither('cell reexecute');\n", | |
" return;\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
" \n", | |
" const cellDeleteDetector = function(mutationsList, observer) {\n", | |
" for (let mutation of mutationsList) {\n", | |
" if (mutation.type === 'childList') {\n", | |
" for (let node of mutation.removedNodes) {\n", | |
" if (node === cellDiv) {\n", | |
" onEither('cell delete')\n", | |
" return;\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" }\n", | |
" }\n", | |
" };\n", | |
"\n", | |
" const outputMutationObserver = new MutationObserver(reexecuteDetector);\n", | |
" const notebookMutationObserver = new MutationObserver(cellDeleteDetector);\n", | |
" \n", | |
" onEither = (reason) => {\n", | |
" outputMutationObserver.disconnect();\n", | |
" notebookMutationObserver.disconnect();\n", | |
" cb(reason);\n", | |
" }\n", | |
" \n", | |
" outputMutationObserver.observe(outputDiv, { childList: true });\n", | |
" notebookMutationObserver.observe(notebookContainer, { childList: true });\n", | |
"}\n", | |
"\n", | |
"window.onCellReexecuteOrDelete = onCellReexecuteOrDelete;" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div id=\"wheeeeee\">\n", | |
"hello\n", | |
"</div>\n", | |
"\n", | |
"<script>\n", | |
"onCellReexecuteOrDelete(document.getElementById('wheeeeee'), (reason) => console.log(\"cleanup because\", reason))\n", | |
"</script>\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"%%html\n", | |
"<div id=\"wheeeeee\">\n", | |
"hello\n", | |
"</div>\n", | |
"\n", | |
"<script>\n", | |
"onCellReexecuteOrDelete(document.getElementById('wheeeeee'), (reason) => console.log(\"cleanup because\", reason))\n", | |
"</script>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<div id=\"another-one\">\n", | |
"hello\n", | |
"</div>\n", | |
"\n", | |
"<script>\n", | |
"onCellReexecuteOrDelete(document.getElementById('another-one'), (reason) => console.log(\"cleanup because\", reason))\n", | |
"</script>\n" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"%%html\n", | |
"<div id=\"another-one\">\n", | |
"hello\n", | |
"</div>\n", | |
"\n", | |
"<script>\n", | |
"onCellReexecuteOrDelete(document.getElementById('another-one'), (reason) => console.log(\"cleanup because\", reason))\n", | |
"</script>" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.7.3" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment