Mutation Observer API

I need to learn about the Mutation Observer API in order to better implement the embedding of social media posts. Right now, social media posts are wrapped in a wrapper div that has a min-height property set on it to prevent too much cumulative layout shift. I want to remove that min-height from the wrapper div after the embedded post is rendered to make the application more responsive.

Date Created:
Last Edited:

References



Notes


The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which was part of the DOM3 Events specification.


Constructor

  • MutationObserver()
    • Creates and returns a new MutationObserver which will invoke a specified callback function when DOM changes occur.
    • DOM observation does not begin immediately; the observe() method must be called first to establish which portion of the DOM to watch and what kind of changes to watch for.
new MutationObserver(callback)	
    • The callback parameter above will be called on each DOM change that qualifies given the observed node or subtree and options.
    • The callback function takes two input parameters:
      • An array of MutationRecord objects, describing each change that occurred.
      • The MutationObserver which invoked the callback. This is most often used to disconnect the observer using MutationObserver.disconnect().


MutationRecord

  • The MutationRecord is a read-only interface that represents an individual DOM mutation observed by a MutationObserver. It is the object inside the array passed to the callback of a MutationObserver.

Instance Properties

  • MutationRecord.addedNodes
    • The nodes added by a mutation. Will be an empty NodeList is no nodes were added.
  • MutationRecord.attributeName
    • The name of the changed attribute as a string, or null.
  • MutationRecord.attributeNamespace
    • The namespace of the changed attribute as a string, or null
  • MutationRecord.nextSibling
    • The next sibling of the added or removed nodes, or null
  • MutationRecord.oldValue
    • The value depends on MutationRecord.type:
      • For attributes, it is the value of the changed attribute before the change
      • For characterData, it is the data of the changed node before the change
      • For childList, it is null
  • MutationRecord.previousSibling
    • The previous sibling of the added or removed nodes, or null
  • MutationRecord.removedNodes
    • The nodes removed by a mutation. Will be an empty NodeList if no nodes were removed
  • MutationRecord.target
    • The node the mutation affected, depending on the MutationRecord.type.
      • For attributes, it is the element whose attribute changed
      • For characterData, it is the CharacterData node
      • For childList, it is the node whose children changed
  • MutationRecord.type
    • A string representing the type of mutation: attributes if the mutation was an attribute mutation, characterData if it was a mutation to a CharacterData node, and childList if it was a mutation to the tree of nodes.


Instance Methods

  • MutationObserver.disconnect()
    • The MutationObserver method disconnect() tells the observer to stop watching for mutations.
    • The observer can be reused by calling its observe() method again.
If the element being observed is removed from the DOM, and then subsequently released by the browser's garbage collection mechanism, the MutationObserver will stop observing the removed element. However, the NutationObserver itself can continue to exist to observe other existing elements.
  • MutationObserver.observe(target, options)
    • The MutationObserver method observe() configures the MutationObserver callback to begin receiving notifications of changed to the DOM that match the given options.
    • Depending on the configuration, the observer may watch a single Node in the DOM tree, or that node and some of its descendant nodes.
    • To stop the MutationObserver (so that none of its callback will be triggered any longer), call MutationObserver.disconnect()
observe(target, options);
    • Parameters
      • target
        • A DOM Node (which may be an Element) within the DOM tree to watch for changes, or to be the root of a subtree of nodes to be watched.
      • options
        • An object providing options that describe which DOM mutations should be reported to mutationObserver's callback. At a minimum, one of childList, attributes, and/or characterData must be true when you call observe(). Otherwise, a TypeError exception will be thrown.
        • subtree
          • Set to true to extend monitoring to the entire subtree of nodes rooted at target. All of the other properties are then extended to all of the nodes in the subtree instead of applying solely to the target node. The default value is false.
        • childList
          • Set to true to monitor the target node (and, if subtree is true, its descendants) for the addition of new child nodes or removal of existing child nodes. The default value is false.
        • attributes
          • Set to true to watch for changed to the value of attributes on the node or nodes being monitored. The default value if true if either of attributeFilter or attributeOldValue is specified, otherwise the default value is false.
        • attributeFilter
          • An array of specific attribute names to be monitored. If this property isn't included, changes to all attributes cause mutation notifications.
        • attributeOldValue
          • Set to true to record the the previous value of any attribute that changes when monitoring the node or nodes for attribute changes. The default value if false.
        • characterData
          • Set to true to monitor the specified target node (and, if subtree is true, its descendants) for changes to the character data contained within the node or nodes. The default value if true if characterDataOldValue is specified, otherwise the default value is false.
        • characterOldValue
          • Set to true to record the previous value of a node's text whenever the text changes on nodes being monitored. The default value is false.
  • MutationObserver.takeRecords()
    • The MutationObsrver method takeRecords() returns a list of all matching DOM changes that have been detected but not yet processed by the observer's callback function, leaving the mutation queue empty.
    • The most common use for this is to immediately fetch all pending mutation records immediately prior to disconnecting the observer, so that any pending mutations can be processed when shutting down the observer.
    • Returns an array of Mutationrecord objects, each describing one change applied to the observed portion of the document's DOM tree.

Exceptions

  • TypeError
    • Thrown in any of the following circumstances:
      • The options are configured such that nothing will actually be monitored. (For example, if childList, attributes, and characterData are all false.)
      • The value of options.attributes is false, but attributeOldValue is true and/or attributeFilter is present.
      • The characterDataOldValue option is true but characterData is false (indicating that character changes are not to be monitored).

Usage Notes

Reusing Mutation Observers
  • You can call observe() multiple times on the same MutationObserver to watch for changes to different parts of the DOM tree and/or different types of changes. There are some caveats to note:
    • If you call observe() on a node that's already being observed by the same MutationObserver, all existing observers are automatically removed from all targets being observed before the new observer is activated.
    • If the same MutationObserver is not already in use on the target, then the existing observers are left alone and the new one is added.


Social Media Embed Mutation Observers


Note: I added a wrapper <div style="display: flex; flex-direction: row; justify-content: center;"> for each of the social media posts so that they are styled correctly. We will use this wrapper for the mutation observer.


X Posts

  • Embedded X Posts initially look similar to below:
<div class="flex-row justify-center" style="min-height: <%=locals.desktop?'762px':'564px'%>;">
  <blockquote class="twitter-tweet"><p lang="en" dir="ltr">A melanistic leopard <br> <a href="https://t.co/tXTSGhqVbL">pic.twitter.com/tXTSGhqVbL</a></p>&mdash; Science girl (@gunsnrosesgirl3) <a href="https://twitter.com/gunsnrosesgirl3/status/1808192447630942582?ref_src=twsrc%5Etfw">July 2, 2024</a></blockquote>
</div>
  • After the window.twttr.widgets.load(); function is called, embedded X posts look similar to below:
<div class="flex-row justify-center" style="min-height: 762px;">
<div class="twitter-tweet twitter-tweet-rendered" style="display: flex; max-width: 550px; width: 100%; margin-top: 10px; margin-bottom: 10px;"><iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" allowfullscreen="true" class="" title="X Post" src="https://platform.twitter.com/embed/Tweet.html?dnt=false&amp;embedId=twitter-widget-0&amp;features=eyJ0ZndfdGltZWxpbmVfbGlzdCI6eyJidWNrZXQiOltdLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X2ZvbGxvd2VyX2NvdW50X3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9iYWNrZW5kIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19yZWZzcmNfc2Vzc2lvbiI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfZm9zbnJfc29mdF9pbnRlcnZlbnRpb25zX2VuYWJsZWQiOnsiYnVja2V0Ijoib24iLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X21peGVkX21lZGlhXzE1ODk3Ijp7ImJ1Y2tldCI6InRyZWF0bWVudCIsInZlcnNpb24iOm51bGx9LCJ0ZndfZXhwZXJpbWVudHNfY29va2llX2V4cGlyYXRpb24iOnsiYnVja2V0IjoxMjA5NjAwLCJ2ZXJzaW9uIjpudWxsfSwidGZ3X3Nob3dfYmlyZHdhdGNoX3Bpdm90c19lbmFibGVkIjp7ImJ1Y2tldCI6Im9uIiwidmVyc2lvbiI6bnVsbH0sInRmd19kdXBsaWNhdGVfc2NyaWJlc190b19zZXR0aW5ncyI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdXNlX3Byb2ZpbGVfaW1hZ2Vfc2hhcGVfZW5hYmxlZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9LCJ0ZndfdmlkZW9faGxzX2R5bmFtaWNfbWFuaWZlc3RzXzE1MDgyIjp7ImJ1Y2tldCI6InRydWVfYml0cmF0ZSIsInZlcnNpb24iOm51bGx9LCJ0ZndfbGVnYWN5X3RpbWVsaW5lX3N1bnNldCI6eyJidWNrZXQiOnRydWUsInZlcnNpb24iOm51bGx9LCJ0ZndfdHdlZXRfZWRpdF9mcm9udGVuZCI6eyJidWNrZXQiOiJvbiIsInZlcnNpb24iOm51bGx9fQ%3D%3D&amp;frame=false&amp;hideCard=false&amp;hideThread=false&amp;id=1808192447630942582&amp;lang=en&amp;origin=https%3A%2F%2Ffrankmbrown.net%2F&amp;sessionId=a78d19810bc9ae1b8fd0e93ce8766b76c9ab481e&amp;theme=light&amp;widgetsVersion=2615f7e52b7e0%3A1702314776716&amp;width=550px" style="position: static; visibility: visible; width: 550px; height: 753px; display: block; flex-grow: 1;" data-tweet-id="1808192447630942582"></iframe></div>
</div>


TikTok Videos

  • Embedded TikTok videos initially look similar to below:
<div class="flex-row justify-center" style="min-height:750px;">
  <blockquote class="tiktok-embed" cite="https://www.tiktok.com/@scout2015/video/6718335390845095173" data-video-id="6718335390845095173" data-embed-from="oembed" style="max-width:605px; min-width:325px;"> <section> <a target="_blank" title="@scout2015" href="https://www.tiktok.com/@scout2015?refer=embed">@scout2015</a> <p>Scramble up ur name & I’ll try to guess it😍❤️ <a title="foryoupage" target="_blank" href="https://www.tiktok.com/tag/foryoupage?refer=embed">#foryoupage</a> <a title="petsoftiktok" target="_blank" href="https://www.tiktok.com/tag/petsoftiktok?refer=embed">#petsoftiktok</a> <a title="aesthetic" target="_blank" href="https://www.tiktok.com/tag/aesthetic?refer=embed">#aesthetic</a></p> <a target="_blank" title="♬ original sound - tiff" href="https://www.tiktok.com/music/original-sound-6689804660171082501?refer=embed">♬ original sound - tiff</a> </section> </blockquote>
</div>
  • After the window.tiktokEmbed.lib.render(HTMLElement); function is called, embedded TikTok videos look similar to below:
<div class="flex-row justify-center" style="min-height:750px;">
<blockquote class="tiktok-embed" cite="https://www.tiktok.com/@scout2015/video/6718335390845095173" data-video-id="6718335390845095173" data-embed-from="oembed" style="max-width:605px; min-width:325px;" id="v85528206007543570"> <iframe name="__tt_embed__v85528206007543570" sandbox="allow-popups allow-popups-to-escape-sandbox allow-scripts allow-top-navigation allow-same-origin" src="https://www.tiktok.com/embed/v2/6718335390845095173?lang=en-US&amp;referrer=https%3A%2F%2Ffrankmbrown.net%2Fprojects%2Fembed-social-media-posts&amp;embedFrom=oembed" style="width: 100%; height: 740px; display: block; visibility: unset;"></iframe></blockquote>
</div>


Instagram Posts

  • Embedded Instagram Posts initially look similar to below:
<div class="flex-row justify-center" style="min-height: <%=locals.desktop?'730px':'540px'%>;">
  <blockquote class="instagram-media" data-instgrm-captioned data-instgrm-permalink="https://www.instagram.com/p/C4gH3z_Jg1D/?utm_source=ig_embed&amp;utm_campaign=loading" data-instgrm-version="14" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a href="https://www.instagram.com/p/C4gH3z_Jg1D/?utm_source=ig_embed&amp;utm_campaign=loading" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div> <div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;">View this post on Instagram</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;"></div></div></a><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/C4gH3z_Jg1D/?utm_source=ig_embed&amp;utm_campaign=loading" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by @americanrivieraorchard</a></p></div></blockquote>
</div>
  • After the window.instgrm.Embeds.process(); function is called, embedded Instagram posts look similar to below:
<div class="flex-row justify-center" style="min-height: 730px;">
<iframe class="instagram-media instagram-media-rendered" id="instagram-embed-0" src="https://www.instagram.com/p/C4gH3z_Jg1D/embed/captioned/?cr=1&amp;v=14&amp;wp=675&amp;rd=https%3A%2F%2Ffrankmbrown.net&amp;rp=%2Fprojects%2Fembed-social-media-posts#%7B%22ci%22%3A0%2C%22os%22%3A1118753.5%2C%22ls%22%3A117.30000001192093%2C%22le%22%3A117.30000001192093%7D" allowtransparency="true" allowfullscreen="true" frameborder="0" height="722" data-instgrm-payload-id="instagram-media-payload-0" scrolling="no" style="background: white; max-width: 540px; width: calc(100% - 2px); border-radius: 3px; border: 1px solid rgb(219, 219, 219); box-shadow: none; display: block; margin: 0px 0px 12px; min-width: 326px; padding: 0px;"></iframe>
</div>


Reddit Comments

  • Embedded Reddit comments initially look similar to below:
<div class="flex-row justify-center" style="min-height: 404px;">
  <blockquote class="reddit-embed-bq" data-embed-height="404"><a href="https://www.reddit.com/r/france/comments/1duaao0/comment/lbf52g6/">Comment</a><br> by<a href="https://www.reddit.com/user/Thor1noak/">u/Thor1noak</a> from discussion<a href="https://www.reddit.com/r/france/comments/1duaao0/ipsos_sociologie_des_électorats_législatives_2024/"><no value=""></no></a><br> in<a href="https://www.reddit.com/r/france/">france</a></blockquote>
</div>
  • After the Reddit embed script is executed, embedded Reddit comments look similar to below:
<div class="flex-row justify-center" style="min-height: 404px;">
<iframe height="404" src="https://embed.reddit.com/r/france/comments/1duaao0/comment/lbf52g6/?embed=true&amp;ref_source=embed&amp;ref=share&amp;utm_medium=widgets&amp;utm_source=embedv2&amp;utm_term=23&amp;showmedia=false&amp;showmore=false&amp;depth=1&amp;utm_name=comment_embed&amp;embed_host_url=https%3A%2F%2Ffrankmbrown.net%2Fprojects%2Fembed-social-media-posts" width="640px" scrolling="no" allowfullscreen="true" sandbox="allow-scripts allow-same-origin allow-popups" allow="clipboard-read; clipboard-write" style="border: none; max-width: 100%; border-radius: 8px; display: block; margin: 0px auto;"></iframe>
</div>


Final Code

The MutatuonObserver turned out to not be the best option for removing the min-height from the wrapper <div>. Instead, you can check with javascript and setTimeout or setInterval for when the <iframe> is rendered and then remove the min-height from the wrapper <div>.


Comments

You must be logged in to post a comment!

Insert Math Markup

ESC
About Inserting Math Content
Display Style:

Embed News Content

ESC
About Embedding News Content

Embed Youtube Video

ESC
Embedding Youtube Videos

Embed TikTok Video

ESC
Embedding TikTok Videos

Embed X Post

ESC
Embedding X Posts

Embed Instagram Post

ESC
Embedding Instagram Posts

Insert Details Element

ESC

Example Output:

Summary Title
You will be able to insert content here after confirming the title of the <details> element.

Insert Table

ESC
Customization
Align:
Preview:

Insert Horizontal Rule

#000000

Preview:


Insert Chart

ESC

View Content At Different Sizes

ESC

Edit Style of Block Nodes

ESC

Edit the background color, default text color, margin, padding, and border of block nodes. Editable block nodes include paragraphs, headers, and lists.

#ffffff
#000000

Edit Selected Cells

Change the background color, vertical align, and borders of the cells in the current selection.

#ffffff
Vertical Align:
Border
#000000
Border Style:

Edit Table

ESC
Customization:
Align:

Upload Lexical State

ESC

Upload a .lexical file. If the file type matches the type of the current editor, then a preview will be shown below the file input.

Upload 3D Object

ESC

Upload Jupyter Notebook

ESC

Upload a Jupyter notebook and embed the resulting HTML in the text editor.

Insert Custom HTML

ESC

Edit Image Background Color

ESC
#ffffff

Insert Columns Layout

ESC
Column Type:

Select Code Language

ESC
Select Coding Language