diff --git a/components/Accordion.jsx b/components/Accordion.jsx
index b8a8916..795538e 100644
--- a/components/Accordion.jsx
+++ b/components/Accordion.jsx
@@ -1,10 +1,61 @@
-import React from "react";
+import React, { useRef, useEffect } from "react";
-const Accordion = ({ children, title = "More" }) => (
-
- {title}
- {children}
-
-);
+const Accordion = ({ children, title = "More" }) => {
+ const detailsRef = useRef(null);
+
+ function openAccordionOnHash() {
+ const hash = location.hash.substring(1);
+ if (hash.length === 0) {
+ return;
+ }
+ let targetElement;
+ targetElement = document.getElementById(hash);
+ if (targetElement === null) {
+ return;
+ }
+
+ // 1. If target is within current details element
+ const detailsElement = detailsRef.current;
+ if (
+ detailsElement !== null &&
+ detailsElement.contains(targetElement)
+ ) {
+ // 2. and it is not open,
+ if (!detailsElement.open) {
+ // 3. Then open it
+ detailsElement.open = true;
+ }
+ // 4. and scroll into view and focus
+ targetElement.focus();
+ targetElement.scrollIntoView({ block: "center" });
+ // Increased for mobile browsers
+ const scrollRestorationDelay = 1000; // 1s
+ setTimeout(() => {
+ if ("scrollRestoration" in history) {
+ history.scrollRestoration = "auto";
+ }
+ }, scrollRestorationDelay);
+ }
+ }
+
+ useEffect(() => {
+ // 1. Add event listener for future hash changes
+ window.addEventListener("hashchange", openAccordionOnHash);
+ // 2. Do it anyway now if initial url has hash
+ openAccordionOnHash()
+
+ // Cleanup - remove listener
+ return () => {
+ window.removeEventListener("hashchange", openAccordionOnHash);
+ };
+ }, []);
+
+ return (
+
+ {title}
+ {children}
+
+ );
+};
export default Accordion;
diff --git a/components/Admonition.jsx b/components/Admonition.jsx
index 83bf86d..53aa382 100644
--- a/components/Admonition.jsx
+++ b/components/Admonition.jsx
@@ -2,7 +2,7 @@ import React, { Component } from "react";
export default class Admonition extends Component {
render() {
- const { type, iconType, title, children, addIfmClass } = this.props;
+ const { type, iconType, title, id, children, addIfmClass } = this.props;
let wrapperClasses = ["admonition", `admonition-${type}`];
if (addIfmClass) {
@@ -11,7 +11,7 @@ export default class Admonition extends Component {
}
return (
-
+
{returnIcon(type, iconType)}
diff --git a/components/InternalLink.jsx b/components/InternalLink.jsx
new file mode 100644
index 0000000..43d0a4b
--- /dev/null
+++ b/components/InternalLink.jsx
@@ -0,0 +1,24 @@
+import React from "react";
+
+/*
+InternalLink should be only used to link to fragment urls placed within a Accordion, in the same page.
+`href` props should only contain a fragment prefixed with "link-" (start with "#link-")
+It allows all the same props as tag.
+*/
+const InternalLink = ({ href, className = "navlink", ...otherProps }) => {
+ if (!href.startsWith("#link-")) {
+ throw new TypeError(
+ "InternalLink - href must start with #. It should only be used for same-page fragment links."
+ );
+ }
+
+ const handleLink = () => {
+ // manually change hash to trigger hashChange event.
+ window.location.hash = "#";
+ window.location.hash = href;
+ };
+
+ return ;
+};
+
+export default InternalLink;
diff --git a/docs/create-modify-element.mdx b/docs/create-modify-element.mdx
index 9b6aafa..a8f5f0b 100644
--- a/docs/create-modify-element.mdx
+++ b/docs/create-modify-element.mdx
@@ -7,8 +7,14 @@ slug: /
import Admonition from "../components/Admonition"
import Accordion from "../components/Accordion"
+import InternalLink from "../components/InternalLink"
import MDN from "../components/MDNBadge"
+:::note DOM interfaces
+Every kind of **DOM** node is represented by an interface based on **Node** interface.
+**Element, Document and DocumentFragment** interfaces inherit from Node. More about DOM interfaces [here](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model)
+:::
+
## Finding elements
### 1. querySelector()
@@ -24,19 +30,19 @@ document.querySelector(".not-found"); // null
```
-
- This method is also available on all DOM nodes. If you want to look within a specific element, just use `ele.querySelector()` on the parent element.
+
+ Besides Document, this method is also available on Element and DocumentFragment.
+ If you want to look within a specific element, just use ele.querySelector() on the parent element.
+ If you want to look within a specific fragment, just use df.querySelector() on the parent fragment.
```js
- // Example - .outer > .inner
- outerEle.addEventListener((ev) => {
- let innerEle = ev.target.querySelector(".inner");
- });
+ // Example - outer is a parent element/fragment.
+ let innerEle = outer.querySelector(".inner");
```
-
+
### 2. querySelectorAll()
@@ -60,17 +66,21 @@ for (let p of paras) {
}
```
-
+`querySelectorAll` is also available on Element and DocumentFragment, in the same way as querySelector.
+
+
## Create / insert element
### 3. createElement()
-It creates a new HTML element, taking the tag name (`"div"`) as argument.
+`document.createElement(tagName) : Element`
+
+This method creates a new HTML element, taking the specified HTML tag name as argument.
```js
document.createElement("div")
-// creates
+// creates a element
```
@@ -94,7 +104,7 @@ document.createElement("div")
`Element.setAttribute(name, value)`
-It sets a attribute on the current element.
+This method sets an attribute on the current element.
`name` - name of the attribute to add. Ex- "type".
`value` - value of the attribute. Ex- "text".
@@ -110,9 +120,9 @@ ele.setAttribute("type", "text")
### 5. appendChild()
-`Element.appendChild(newElement)`
+`Node.appendChild(newNode)`
-It inserts a new element as the last child of the parent element.
+This method inserts a new node as the last child of the parent node.
```js
let eleDiv = document.createElement("div")
@@ -124,13 +134,13 @@ eleDiv.appendChild(eleH1)
- If newElement is already a child of another element,
+ If newNode is already a child of another node,
then it is removed from its existing parent and then added to the new parent.
-
- If newElement is a DocumentFragment, then - instead of adding the fragment itself as a child,
- the entire content of the fragment is added as children of the parent element.
+
+ If newNode is a DocumentFragment, then - instead of adding the fragment itself as a child,
+ the entire content of the fragment is added as children of the parent node.
@@ -150,11 +160,11 @@ eleDiv.appendChild(eleH1)
### 6. removeChild()
-`Element.removeChild(childElement)`
+`Node.removeChild(childNode)`
-This method tries to remove a child element from the parent and returns the removed node.
+This method tries to remove a child node from the parent and returns the removed node.
-If the element to be removed is not a child of the parent element, it throws a `NotFoundError` DOMException.
+If the node to be removed is not a child of the parent node, it throws a `NotFoundError` DOMException.
```js
//
@@ -168,13 +178,15 @@ parentEle.removeChild(childEle)
### 7. insertBefore()
-`Element.insertBefore(newEle, refEle)`
+`Node.insertBefore(newEle, refEle)`
-This method inserts a new child element before another child within a parent.
+This method inserts a new child node before another node within a parent.
+
+It also works with **DocumentFragment**, by inserting all of its content in that position.
`newEle` - The node to be inserted.
-`refEle` - The reference element before which newEle should be inserted. If refEle is `null`, then newEle is added as the last child.
+`refEle` - The reference node before which newEle should be inserted. If refEle is `null`, then newEle is added as the last child.
```js
//
@@ -204,18 +216,14 @@ parent.insertBefore(newEle, ref.nextSibling)
### 8. Document Fragment
+`document.createDocumentFragment() : DocumentFragment`
+
+This method is used to create a new empty document fragment.
+
DocumentFragment can be considered to be a off-screen lightweight `document` object.
It is generally used for batching multiple operations - by doing them in the fragment over time and then rendering the whole content to the visible DOM in one shot .
-Because fragment is not rendered, making changes to it doesn't cause any performance impact (no reflow).
-
-**Methods** -
-
-`document.createDocumentFragment()` - creates a new empty fragment.
-
-`.appendChild()` / `.insertBefore()` - can be used to move the whole content of the fragment to the actual DOM.
-
-**Example** -
+Because fragment is not rendered, making changes to it doesn't cause any performance impact (no reflow). To **actually "render" the fragment** in the visible document, use appendChild or `insertBefore`.
```js
let fragment = document.createDocumentFragment()
@@ -236,4 +244,4 @@ outerEle.appendChild(fragment)
--------
-In the next section, we'll see how to read information from a DOM element.
\ No newline at end of file
+In the next section, we'll see how to read information from a DOM element.
diff --git a/docusaurus.config.js b/docusaurus.config.js
index 69faacd..1c9b7a7 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -102,4 +102,10 @@ module.exports = {
},
],
],
+ scripts: [
+ {
+ src:
+ '/scripts/handleScrollRestoration.js',
+ },
+ ]
};
diff --git a/src/css/custom.css b/src/css/custom.css
index 5ed4a52..86277d8 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -32,4 +32,16 @@
.admonition h5 {
text-transform: unset;
+}
+
+.navlink{
+ cursor: pointer;
+}
+
+.navlink:hover{
+text-decoration: underline;
+}
+
+summary {
+ cursor: pointer;
}
\ No newline at end of file
diff --git a/static/scripts/handleScrollRestoration.js b/static/scripts/handleScrollRestoration.js
new file mode 100644
index 0000000..bd93e7c
--- /dev/null
+++ b/static/scripts/handleScrollRestoration.js
@@ -0,0 +1,8 @@
+var hash = window.location.hash;
+var hashName = hash.substring(1, hash.length);
+
+if (hashName.startsWith("link-")) {
+ if ("scrollRestoration" in history) {
+ history.scrollRestoration = "manual";
+ }
+}