Skip to content

Commit f32ad66

Browse files
authored
v3.0.0 Meta tags support
v3.0.0 Meta tags support
2 parents 56a9e37 + fa4a25e commit f32ad66

File tree

9 files changed

+110
-25
lines changed

9 files changed

+110
-25
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ browser to localhost:3210.
2727

2828
Use the `ScrollableSection` tag to wrap any React element(s), making it a scrollable anchor.
2929
Use the `ScrollableLink` to wrpar a link to the corresponding section.
30-
You may also set a `title` attribute if you want to update a document title.
30+
You may also set a `meta` attribute if you want to update a document title and meta-tags.
3131

3232
```js
3333
import React, { Component } from 'react';
@@ -45,11 +45,11 @@ export default class Page extends Component {
4545
<a> Go to section 2 </a>
4646
</ScrollableLink>
4747

48-
<ScrollableSection name={'section1'} title="Section 1">
48+
<ScrollableSection name={'section1'} meta={title: 'Section 1'} >
4949
<div> Hello World </div>
5050
</ScrollableSection>
5151

52-
<ScrollableSection name={'section2'} title="Section 2">
52+
<ScrollableSection name={'section2'} meta={title: 'Section 2'}>
5353
<div> How are you world? </div>
5454
</ScrollableSection>
5555
</div>
@@ -132,6 +132,7 @@ configureAnchors({offset: 60})
132132
| `scrollBehaviour` | `'smooth'` | Can be `'smooth'`, `'instant'` and `'auto'`.
133133
| `scrollOnImagesLoad` | `false` | Wait until all the images are loaded before scrolling to the section on page load.
134134
| `onSectionEnter` | `null` | An event that is fired when user reaches to some another section. There are two attributes: `onSectionEnter(newState, oldState)`
135+
| `meta` | `null` | An object that may contain default title and meta-tags to set on page load. e.g. `meta: {title: 'Hello', description: 'World'}`
135136
136137
### 3. Utilities
137138

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-update-url-on-scroll",
3-
"version": "2.2.0",
3+
"version": "3.0.0",
44
"description": "Provide updating the URL/hash on page scrolling.",
55
"main": "lib/index.js",
66
"files": [

src/Manager.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { debounce } from './utils/func';
1+
import { debounce, getAnchoreByName } from './utils/func';
22
import { getBestAnchorGivenScrollLocation, getScrollTop } from './utils/scroll';
33
import { getHash, updateHash, removeHash } from './utils/hash';
4+
import { setMetaTags, getDefaultMetaTags } from './utils/meta';
45

56
const defaultConfig = {
67
affectHistory: false,
@@ -10,7 +11,8 @@ const defaultConfig = {
1011
scrollBehaviour: 'smooth',
1112
scrollDelay: 0,
1213
scrollOnImagesLoad: false,
13-
onSectionEnter: null
14+
onSectionEnter: null,
15+
meta: null
1416
}
1517

1618
const EVENT_IMAGES_LOADED = 'images:loaded';
@@ -26,9 +28,10 @@ class Manager {
2628

2729
this.basePath = this.getBasePath();
2830
this.basePathName = window.location.pathname;
29-
this.baseTitle = document.title;
3031
this.imagesAreLoaded = false;
3132

33+
this.resetDefaultMetaTags();
34+
3235
setTimeout(() => {
3336
if (this.config.scrollOnImagesLoad) {
3437
const imgs = document.images;
@@ -86,6 +89,20 @@ class Manager {
8689
...defaultConfig,
8790
...config
8891
}
92+
this.resetDefaultMetaTags();
93+
}
94+
95+
resetDefaultMetaTags = () => {
96+
if (this.config.meta) {
97+
this.defaultMetaTags = getDefaultMetaTags(this.config.meta);
98+
setMetaTags(this.defaultMetaTags);
99+
} else {
100+
this.defaultMetaTags = getDefaultMetaTags();
101+
}
102+
}
103+
104+
setDefaultMetaTags = () => {
105+
setMetaTags(this.defaultMetaTags);
89106
}
90107

91108
goToTop = () => {
@@ -97,7 +114,7 @@ class Manager {
97114
});
98115
}
99116

100-
addAnchor = ({element, name, hash, id, title, exact}) => {
117+
addAnchor = ({element, name, hash, id, meta, exact}) => {
101118
// if this is the first anchor, set up listeners
102119
if (Object.keys(this.anchors).length === 0) {
103120
this.addListeners();
@@ -118,9 +135,29 @@ class Manager {
118135
component: element,
119136
name,
120137
hash,
121-
title,
138+
meta,
122139
exact
123140
};
141+
142+
this.normalizeMetaTags();
143+
}
144+
145+
normalizeMetaTags = () => {
146+
Object.keys(this.anchors).forEach(anchorId => {
147+
const anchor = this.anchors[anchorId];
148+
if (anchor.hash && !anchor.meta) {
149+
150+
if (anchor.exact || !anchor.name) {
151+
anchor.meta = this.defaultMetaTags;
152+
} else if (anchor.name) {
153+
const parentAnchor = getAnchoreByName(this.anchors, anchor.name);
154+
155+
if (parentAnchor) {
156+
anchor.meta = parentAnchor.meta;
157+
}
158+
}
159+
}
160+
});
124161
}
125162

126163
removeAnchor = (id) => {

src/ScrollableSection.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@ export default class ScrollableSection extends Component {
1313
]),
1414
name: PropTypes.string,
1515
hash: PropTypes.string,
16-
title: PropTypes.string,
17-
formatTitle: PropTypes.func,
1816
onEnter: PropTypes.func
1917
}
2018

2119
constructor(props) {
2220
super(props);
2321
this.name = (props.name || '').replace(/^\//, '') || null;
2422
this.hash = (props.hash || '').replace(/^\#/, '') || props.children.ref || null;
25-
this.title = props.title || null;
23+
this.meta = props.meta || null;
2624
this.id = createId({name: this.name, hash: this.hash});
2725
}
2826

@@ -35,7 +33,7 @@ export default class ScrollableSection extends Component {
3533
hash: this.hash,
3634
exact: !!this.props.exact,
3735
id: this.id,
38-
title: this.title ? this.props.formatTitle(document.title, this.title) : null
36+
meta: this.meta
3937
});
4038
}
4139

@@ -65,7 +63,7 @@ export default class ScrollableSection extends Component {
6563
}
6664

6765
ScrollableSection.defaultProps = {
68-
formatTitle: (baseTitle, segmentTitle) => `${baseTitle} / ${segmentTitle}`
66+
6967
};
7068

7169

src/index.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import Manager from './Manager'
2-
export const goToTop = Manager.goToTop
3-
export const configureAnchors = Manager.configure
1+
import Manager from './Manager';
2+
export const goToTop = Manager.goToTop;
3+
export const configureAnchors = Manager.configure;
44

5-
export { updateHash as goToAnchor, removeHash } from './utils/hash'
5+
export { setMetaTags } from './utils/meta';
6+
export { updateHash as goToAnchor, removeHash } from './utils/hash';
67
export { default, ScrollableLink } from './ScrollableSection'

src/utils/func.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,8 @@ export const debounce = (func, wait, immediate) => {
1919
}
2020

2121
export const createId = ({name, hash}) => `___scroll-section___${name || ''}___${hash || ''}`;
22+
23+
export const getAnchoreByName = (object = {}, name) => {
24+
const key = Object.keys(object).find(key => object[key].name === name && !object[key].hash);
25+
return key ? object[key] : null;
26+
}

src/utils/hash.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {createId} from './func';
2+
import {setMetaTags} from './meta';
23

34
const basePath = `${window.location.origin}${window.location.pathname}`;
4-
const baseTitle = document.title;
55

66
const getCurrentHash = () => decodeURI(window.location.hash.slice(1));
77

@@ -13,25 +13,28 @@ export const getHash = ({manager}) => {
1313
}
1414

1515
export const updateHash = ({anchor, affectHistory, manager}) => {
16-
const {hash, name, title, exact} = anchor;
16+
const {hash, name, meta, exact} = anchor;
1717
const {basePath} = manager;
1818
const method = affectHistory ? 'pushState' : 'replaceState';
1919
const newPath = `${name ? `${exact ? window.location.origin : basePath}/${name}` : basePath}${hash ? `#${hash}` : ''}`;
2020

2121
window.history[method](undefined, undefined, newPath);
2222

23-
if (title) {
24-
document.title = title;
23+
if (meta) {
24+
setMetaTags(meta);
25+
console.log('SET', meta);
26+
} else {
27+
manager.setDefaultMetaTags();
2528
}
2629
}
2730

2831
// remove hash in url without affecting history or forcing reload
2932
export const removeHash = ({manager}) => {
3033
window.history.replaceState(
3134
undefined,
32-
baseTitle,
35+
manager.defaultMetaTags.title,
3336
manager ? manager.basePath : basePath
3437
);
3538

36-
document.title = baseTitle;
39+
manager.setDefaultMetaTags();
3740
}

src/utils/meta.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const getMeta = (metaName) => {
2+
const metas = document.getElementsByTagName('meta');
3+
return Array.from(metas).find(item => item.getAttribute('name') === metaName);
4+
}
5+
6+
export const setMetaTags = (metaTagsList = {}) => {
7+
const {title, ...metaTags} = metaTagsList;
8+
9+
if (title) {
10+
document.title = title;
11+
}
12+
13+
Object.keys(metaTags).forEach(tagName => {
14+
const currentTag = getMeta(tagName);
15+
16+
if (currentTag) {
17+
// update a meta tag
18+
currentTag.setAttribute('content', metaTags[tagName]);
19+
} else {
20+
// create a meta tag
21+
const meta = document.createElement('meta');
22+
meta.name = tagName;
23+
meta.setAttribute('content', metaTags[tagName]);
24+
document.getElementsByTagName('head')[0].appendChild(meta);
25+
}
26+
});
27+
28+
}
29+
30+
export const getDefaultMetaTags = (metaTags) => {
31+
if(metaTags) {
32+
return metaTags;
33+
}
34+
35+
const metas = document.getElementsByTagName('meta');
36+
37+
return Array.from(metas).reduce((acc, item) => {
38+
return {...acc, [item.getAttribute('name')]: item.getAttribute('content')}
39+
}, { title: document.title });
40+
}

0 commit comments

Comments
 (0)