Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add elementReady.subscribe() #24

Closed
wants to merge 8 commits into from
Closed

Conversation

qti3e
Copy link

@qti3e qti3e commented Jul 1, 2019

This approach uses a WeakMap to store already seen elements (to prevent memory leak) in order to detect new elements, also because checking all the elements to find out new ones might be a bit heavy we do this in half of the requestAnimationFrames, feedback is welcome.

Fixes #22


IssueHunt Summary

Referenced issues

This pull request has been submitted to:


IssueHunt has been backed by the following sponsors. Become a sponsor

index.js Outdated Show resolved Hide resolved
index.js Outdated Show resolved Hide resolved
*/
stop(): void;
};

type StoppablePromise<T> = Promise<T> & {
Copy link
Collaborator

@fregante fregante Jul 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, but the description doesn't match.

 	type StoppablePromise<T> = Promise<T> & Stoppable;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually wanted to do so, but I didn't want to change the doc comments.

@qti3e
Copy link
Author

qti3e commented Jul 7, 2019

@bfred-it Hey! I've made a small change and I guess this PR is ready to be merged.
Please let me know your opinion.

index.js Outdated
if (checkFrame) {
const allElements = target.querySelectorAll(selector);

for (let i = 0; i < allElements.length; ++i) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use a for-of loop.

index.js Outdated
@@ -54,4 +54,52 @@ const elementReady = (selector, {
return Object.assign(promise, {stop});
};

elementReady.subscribe = (selector, cb, {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use abbreviations. cb => callback.

index.test-d.ts Outdated Show resolved Hide resolved
readme.md Outdated

Same as `elementReady` options.

#### elementReady.subscribe()#stop()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

test.js Outdated
@@ -187,3 +187,56 @@ test('ensure different promises are returned on second call with the same select
document.querySelector('.unicorn').remove();
t.is(prependElement(), await elementReady('.unicorn'));
});

test('subscribe: check if elements are detected', async t => {
const e = () => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use one character variable names.

### elementReady.subscribe(selector, cb, options?)

Detect elements as they are added to the DOM.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include some use-cases for this method.

index.d.ts Outdated
/**
Called on each new element that matches the selector in the DOM.
*/
type SubscribeCallback<T> = (el: T) => any;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This type is incorrect. According to the JS code, the return value is not used.

Suggested change
type SubscribeCallback<T> = (el: T) => any;
type SubscribeCallback<T> = (element: T) => any;

index.d.ts Outdated
Detect elements as they are added to the DOM.

@param selector - [CSS selector.](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
@param cb Callback that is called for each new element.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@param cb Callback that is called for each new element.
@param cb - Callback that is called for each new element.

index.d.ts Outdated
/**
Stop checking for new elements.

Calling it multiple times does nothing.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect indentation.

index.js Outdated
stopOnDomReady = true,
timeout = Infinity
} = {}) => {
const seen = new WeakSet();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const seen = new WeakSet();
const seenElements = new WeakSet();

@sindresorhus
Copy link
Owner

The implementation looks good, but I think you need to put more effort into the docs and TS definition.

@sindresorhus sindresorhus changed the title Implement elementReady.subscribe Add elementReady.subscribe() Jul 7, 2019
@qti3e
Copy link
Author

qti3e commented Jul 8, 2019

@sindresorhus Sorry for all of this style-guide related issues, I wasn't quite familiar with your coding guide-lines, anyway I've addressed all of your comments.

Also, I made a change to SubscribeOptions, I think SubscribeOptions.stopOnDomReady's default is better to be false as when a user calls this function they want to subscribe to all of the new elements, also they're probably going to use this function after the DOM ready event most of the time.

@fregante
Copy link
Collaborator

fregante commented Jul 8, 2019

  1. My usage would be exactly to catch elements as the page loads, until the DOM ready
  2. It’s best to match the options for the regular elementReady

@qti3e
Copy link
Author

qti3e commented Jul 18, 2019

ping @bfred-it

@sindresorhus
Copy link
Owner

There are still some unresolved PR comments and some space indentation that needs to be converted to tab indentation.

index.test-d.ts Outdated
@@ -12,4 +12,17 @@ expectType<elementReady.StoppablePromise<Element | undefined>>(promise);
expectType<elementReady.StoppablePromise<HTMLDivElement | undefined>>(elementReady('div'));
expectType<elementReady.StoppablePromise<SVGElement | undefined>>(elementReady('text'));


const {stop} = elementReady.subscribe('.unicorn', e => null);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

index.test-d.ts Outdated

elementReady.subscribe('.unicorn', e => expectType<Element>(e));
elementReady.subscribe('div', e => expectType<HTMLDivElement>(e));
elementReady.subscribe('text', e => expectType<SVGElement>(e));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And these callbacks should not be inline arrow functions. They don't return anything so it's clearer with normal arrow functions.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be clear, Sindre means this by "normal arrow functions":

elementReady.subscribe('text', e => {
	expectType<SVGElement>(e);
});

@sindresorhus
Copy link
Owner

@qti3e Ping back :)

@qti3e
Copy link
Author

qti3e commented Feb 17, 2020

@sindresorhus I've been busy working on something, sorry I forgot about this pr since it's been a long time.

selector: string,
callback: elementReady.SubscribeCallback<ElementName>,
options?: elementReady.Options
): Stoppable;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use tab-indentation.

expectType<SVGElement>(element)
});

expectType<() => any>(stop);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. It returns void.

continue;
}

callback(element);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you pass undefined instead of null (which querySelectorAll returns). To be consistent with our other usage of undefined.

/**
Called on each new element that matches the selector in the DOM.
*/
type SubscribeCallback<T> = (element: T) => void;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The element is not guaranteed to exist so should be optional.

@@ -185,3 +185,56 @@ test('ensure different promises are returned on second call with the same select
document.querySelector('.unicorn').remove();
t.is(prependElement(), await elementReady('.unicorn'));
});

test('subscribe: check if elements are detected', async t => {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There needs to be tests for the timeout and stopOnDomReady options with subscribe too.

const seenElements = new WeakSet();

let rafId;
let checkFrame = true;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should should have a should, is, or has prefix. Not clear to me which one fits the best.

@sindresorhus
Copy link
Owner

@qti3e Bump :)

@sindresorhus
Copy link
Owner

Closing for lack of activity.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Subscribe to new elements on the page
3 participants