Merge pull request #3 from lapstjup/interface_func_signature

Added brief on DOM interfaces + func signatures + support hash link to notes
This commit is contained in:
2020-10-28 00:46:22 +05:30
committed by GitHub
7 changed files with 151 additions and 42 deletions
+58 -7
View File
@@ -1,10 +1,61 @@
import React from "react"; import React, { useRef, useEffect } from "react";
const Accordion = ({ children, title = "More" }) => ( const Accordion = ({ children, title = "More" }) => {
<details> const detailsRef = useRef(null);
<summary>{title}</summary>
{children} function openAccordionOnHash() {
</details> 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 (
<details ref={detailsRef}>
<summary>{title}</summary>
{children}
</details>
);
};
export default Accordion; export default Accordion;
+2 -2
View File
@@ -2,7 +2,7 @@ import React, { Component } from "react";
export default class Admonition extends Component { export default class Admonition extends Component {
render() { render() {
const { type, iconType, title, children, addIfmClass } = this.props; const { type, iconType, title, id, children, addIfmClass } = this.props;
let wrapperClasses = ["admonition", `admonition-${type}`]; let wrapperClasses = ["admonition", `admonition-${type}`];
if (addIfmClass) { if (addIfmClass) {
@@ -11,7 +11,7 @@ export default class Admonition extends Component {
} }
return ( return (
<div className={wrapperClasses.join(" ")}> <div tabIndex={0} className={wrapperClasses.join(" ")} id={id}>
<div className="admonition-heading"> <div className="admonition-heading">
<h5> <h5>
<div className="admonition-icon">{returnIcon(type, iconType)}</div> <div className="admonition-icon">{returnIcon(type, iconType)}</div>
+24
View File
@@ -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 <a/> 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 <a onClick={handleLink} className={className} href={href} {...otherProps}></a>;
};
export default InternalLink;
+40 -32
View File
@@ -7,8 +7,14 @@ slug: /
import Admonition from "../components/Admonition" import Admonition from "../components/Admonition"
import Accordion from "../components/Accordion" import Accordion from "../components/Accordion"
import InternalLink from "../components/InternalLink"
import MDN from "../components/MDNBadge" 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 ## Finding elements
### 1. querySelector() ### 1. querySelector()
@@ -24,19 +30,19 @@ document.querySelector(".not-found"); // null
``` ```
<Accordion> <Accordion>
<Admonition type="note" title="Extra"> <Admonition type="note" title="Also available on Element and fragment" id="link-element-has-qs">
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 <b>Document</b>, this method is also available on <b>Element</b> and <b>DocumentFragment</b>.<br/>
If you want to look within a specific element, just use <code>ele.querySelector()</code> on the parent element.<br/>
If you want to look within a specific fragment, just use <code>df.querySelector()</code> on the parent fragment.
```js ```js
// Example - .outer > .inner // Example - outer is a parent element/fragment.
outerEle.addEventListener((ev) => { let innerEle = outer.querySelector(".inner");
let innerEle = ev.target.querySelector(".inner");
});
``` ```
</Admonition> </Admonition>
</Accordion> </Accordion>
<MDN title="querySelector" url="https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector" /> <MDN title="querySelector" url="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector" />
### 2. querySelectorAll() ### 2. querySelectorAll()
@@ -60,17 +66,21 @@ for (let p of paras) {
} }
``` ```
<MDN title="querySelectorAll" url="https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll" /> `querySelectorAll` is also available on <b>Element</b> and <b>DocumentFragment</b>, in the <InternalLink href="#link-element-has-qs">same way as querySelector</InternalLink>.
<MDN title="querySelectorAll" url="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll" />
## Create / insert element ## Create / insert element
### 3. createElement() ### 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 ```js
document.createElement("div") document.createElement("div")
// creates <div/> // creates a <div> element
``` ```
<Accordion title="Related"> <Accordion title="Related">
@@ -94,7 +104,7 @@ document.createElement("div")
`Element.setAttribute(name, value)` `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". `name` - name of the attribute to add. Ex- "type".
`value` - value of the attribute. Ex- "text". `value` - value of the attribute. Ex- "text".
@@ -110,9 +120,9 @@ ele.setAttribute("type", "text")
### 5. appendChild() ### 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 ```js
let eleDiv = document.createElement("div") let eleDiv = document.createElement("div")
@@ -124,13 +134,13 @@ eleDiv.appendChild(eleH1)
<Accordion title="Edge cases and alternative"> <Accordion title="Edge cases and alternative">
<Admonition type="note" title="If it already has a parent"> <Admonition type="note" title="If it already has a parent">
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. then it is removed from its existing parent and then added to the new parent.
</Admonition> </Admonition>
<Admonition type="note" title="If it is DocumentFragment"> <Admonition type="note" title="If it is DocumentFragment" id="link-appendChild-documentFragment">
If newElement is a DocumentFragment, then - instead of adding the fragment itself as a child, 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 element. the entire content of the fragment is added as children of the parent node.
</Admonition> </Admonition>
<Admonition type="note" title=".append()"> <Admonition type="note" title=".append()">
@@ -150,11 +160,11 @@ eleDiv.appendChild(eleH1)
### 6. removeChild() ### 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 ```js
// <div class="parent"> // <div class="parent">
@@ -168,13 +178,15 @@ parentEle.removeChild(childEle)
### 7. insertBefore() ### 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. `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 ```js
// <ul> // <ul>
@@ -204,18 +216,14 @@ parent.insertBefore(newEle, ref.nextSibling)
### 8. Document Fragment ### 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. 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 . 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). 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, <InternalLink href="#link-appendChild-documentFragment">use appendChild</InternalLink> or `insertBefore`.
**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** -
```js ```js
let fragment = document.createDocumentFragment() let fragment = document.createDocumentFragment()
+6
View File
@@ -102,4 +102,10 @@ module.exports = {
}, },
], ],
], ],
scripts: [
{
src:
'/scripts/handleScrollRestoration.js',
},
]
}; };
+12
View File
@@ -33,3 +33,15 @@
.admonition h5 { .admonition h5 {
text-transform: unset; text-transform: unset;
} }
.navlink{
cursor: pointer;
}
.navlink:hover{
text-decoration: underline;
}
summary {
cursor: pointer;
}
@@ -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";
}
}