(Edit on 9-Jun-21: Post title changed from “Hoping to get some expert’s scrutiny” to “Hoping to get some scrutiny from experienced programmers”. People are really modest here…)
This isn’t asking for full on project feedback (my CSS code is far from done), but more asking for experienced programmers’ critical opinions about certain aspects of my JS code. If anyone’s willing to spend the time, I’d appreciate it very much.
Challenge: Build a Random Quote Machine
While coding for this challenge I did my best to follow these rules:
- Use module pattern.
- Write loosely coupled functions.
- Use abstraction.
- Keep it shadow.
- Don’t repeat yourself.
- Don’t reinvent the wheel.
I also added a few challenges on top of merely getting it to work:
- Make no assumptions about the remote server’s configurations.
- Make no assumptions about the structure of the received data payload.
- Make no assumptions about the frontend’s HTML.
Here’s what I ended up with: FCC Project: Random Quote Machine (codepen.io)
I would appreciate some expert’s scrutiny in terms of how well my JS code has adhered to the above principles, as well as any glaring issues you’d hate to see in the real world.
Should you find anything confusing:
Module usage explained
Basic logic
The module has an entry function randomQuoteMachine()
. You call this function to request and store (in an internal state) data from a remote server. Simultaneously, the function returns an object with several exposed methods one of which is .afterDocReady()
.
You then use .afterDocReady()
to:
- map parts of the received data to DOM elements;
- attach event handlers to DOM elements; and
- specify functions to run as soon as the above are done.
Additionally:
- The module has a built-in array index randomiser, or you can supply your own.
- The module provides a built-in tweet intent builder, or you can supply your own.
Methods references
randomQuoteMachine( { url | settings } )
See “Basic logic” above for usage.
-
url
A string specifying the address of the resource to request. If the function is called withurl
, it will assume thatdataType
is'json'
. -
settings
A jQuery ajax settings object. -
The above two are mutually exclusive and one of them must be supplied.
Returns an object which exposes all methods listed below.
IMPORTANT:
The resource payload, upon successful storage, will be logged in the console along with its type for observation. You need to make sure that it’s a JS array or object. If not, you need to use the .data()
method (see below) to manually parse it.
.afterDocReady( mapping [, onLoadHandler1] [, onLoadHandler2]... )
See “Basic logic” above for usage. Returns undefined
.
-
mapping
Mandatory. An object with key-value pairs any of which can take one of these forms:
selector: path
selector: callback
selector: [path, prefix, suffix]
selector: [callback, prefix, suffix]
selector: [event, handler]
The first four forms will map text content to the inner HTML of a DOM element. The last form will attach an event handler to a DOM element.-
selector
A valid jQuery selector. You may need to wrap it in quotes. -
path
A string which takes the style of JS accessors and specifies which part of the received data you’d like to extract, e.g.:
"quotes[flexiIndex1][0].source info[flexiIndex2].text"
, or
".quotes.meta[flexiIndex1][flexiIndex2].details[101].author"
They also follow these rules:-
Dot notations are treated as fixed object locations, e.g.
.source info
will be assumed as trying to access the'source info'
property of an object. With the exception of dots and brackets, there is no need to worry about other illegal characters for dot notation such as space. -
If the full path starts with a dot notation, the leading
.
can be omitted. -
Bracket notations enclosing only numbers are treated as fixed Array locations, e.g.
[101]
will be assumed as trying to access an array at index 101. -
Bracket notations enclosing not only numbers are treated as “flexible” array locations on which the built-in index randomiser can be applied, e.g.
[flexiIndex1]
will result in an internal state name “flexiIndex1” being created and initialised to a random & valid index number; later the index may be used to extract data, or it may regenerate, depending on the relevant method call. -
You can have the same name for flexible array locations in different paths; they will remain equal to each other. If their parent arrays have different lengths, the shorter length will be respected.
-
You cannot have the same name for flexible array locations in a single path.
-
If differently named flexible array locations in one path also both appear in another path, the order they appear in the two paths must be the same, e.g. you cannot have:
{
'#text': 'quotes[flexidex1][flexidex2].quote',
'#author': 'quotes[flexidex2][flexidex1].quote'
}
-
DO NOT use flexible index locations on arrays of length 1 or 0. This will result in infinite loops.
-
-
callback
A function which takes the stored data as it’s argument. You can use this to implement your own data extraction algorithm. -
prefix
A string with which the mapped text content will start. -
suffix
A string with which the mapped text content will end. If you want to supply a suffix but not a prefix, use an empty string for the prefix. -
event
A string specifying any of the browser’s standard JavaScript event type. One of the most often used ones is"click"
. This andhandler
below should follow the exact same syntax as the jQuery.on()
method. -
handler
A function for handling the specified event. This andevent
above should follow the exact same syntax as the jQuery.on()
method.
-
-
onLoadHandler1, onLoadHandler2...
Optional. Functions which will be called (in the order they are supplied) once the DOM tree is ready and if the resource request was successful.
.data( [ argument ] )
As a getter, this method can be called without an argument to return the requested and stored data.
As a setter, this method can be called with an argument to replace the stored data with the passed argument. This also returns the stored data
.refreshTextMapping()
This method literally calls the .safeRandom()
, .runMappingCallbacks()
and .applyTextMapping()
methods described below.
Returns undefined
.
.safeRandom()
Calling this method will refresh all flexible array locations, if any. It’s guaranteed they will be different from the previous locations. Data extracted from the new locations will not be reflected in the DOM until the .applyTextMapping()
method (described below) is called.
Returns undefined
.
The “safe” part of the name refers to the fact that, if arrays of different lengths use the same flexible index location, the shortest length will be respected.
.runMappingCallbacks()
This method will call all callback functions supplied for DOM-data mapping when .afterDocReady()
was called (event handlers will not be called). The returned value of these callbacks will not be reflected in the DOM until the .applyTextMapping()
method (described below) is called.
Returns undefined
.
.applyTextMapping()
This method will update DOM using the latest flexible array locations as well as the latest values returned by callbacks supplied for DOM-data mapping.
.readyTweetIntent( anchorSelector [, mappedElement1 [, option1 ] ]
[ [, separator1 ], mappedElement2 [, option2 ] ]
[ [, separator2 ], mappedElement3 [, option3 ] ]
... )
(This method is available because the FCC project has some Twitter related requirements.)
This method will change the href
attribute of an <a>
element to a tweet intent url with the text parameter prepopulated using the contents of any specified mapped DOM elements.
-
anchorSelector
Mandatory. A string representing a valid jQuery selector. This will be used to select the anchor element. -
mappedElement
Optional. A string representing a valid jQuery selector which you used for DOM-data mapping when calling.afterDocReady()
. The content of the element will be retrieved from an internal state, not from DOM directly. -
option
Optional; must not appear without a precedingmappedElement
.
One of three numbers representing three options:
0
- Do not include the precedingmappedElement
's prefix or suffix in tweet text.
1
- Only include the precedingmappedElement
's prefix in tweet text.
2
- Only include the precedingmappedElement
's suffix in tweet text.
If no supplied, both prefix and suffix will be included in tweet text. -
separator
Optional; should not appear without a succeedingmappedElement
.
A string starting with'@'
, the characters following the leading'@'
will be inserted between the previous and the followingmappedElement
s, e.g. if'@ '
(@ followed by a space) is supplied, a space will be inserted.