#content
¶
…
Contents:
That’s a good question. There are plenty of other good packages out there. Why another?
To be light. To be fast. To be powerful. To be easy to use.
For example, PorterJS does not ship with a templating engine. Out of the box, it does not do client side rendering. HOWEVER, it can be used with your favorite templating engine. Handlebars and mustache.js both plug in nicely. My personal favorite is Markup.js.
Many JS frameworks out there require you to dive in and become an expert in their way of doing things. The goal for PorterJS is to alleviate that. It is meant to be developed to look and feel like plain old JavaScript and HTML, and to operate intuitively.
The project is still in active initial development with not all of its core features completed. However, enough is together to make it usable and operational. For any bugs, please let me know on the GitHub page.
If you are interested in helping to debug, write documentation, test, develop, or just sit down and have a been together, let me know.
Known as The Brewmaster, I am a coder, a lawyer, a father, an entrepreneur, and a lot of other things. Yes, I do like to brew beer, and yes, I do like drinking porters.
PorterJS exposes several convenience wrappers:
p.one()
p.all()
These methods are essentially just wrappers for document.querySelector
and document.querySelectorAll
. However, they can be chained together in any combination, unlike document.querySelector
and document.querySelectorAll
.
p.one('#some_id').all('p') >> OKAY
p.one('#some_id').one('p') >> OKAY
p.all('.some_class').one('p') >> OKAY
p.all('.some_class').all('p') >> OKAY
You can call addEventListener()
and removeEventListener
on an entire group of nodes, unlike in stock JavaScript. So, for example, the following would work as expected.
p.all('.some_class').addEventListener('click', callSomeFunction)
Also, another difference from document.querySelectorAll
and all
is that you can run a forEach
loop or a for-of
loop.
var q = p.all('.some_class');
q.forEach( function (item) {
...
})
for (var item of q) {
...
}
In addition, it also exposes some additional methods that are used under the hood, but can be helpful in creating UI components:
p.trigger_call()
p.ready()
p.debounce()
p.load()
There are several classes that are used under the hood. These too (like the methods above) may be useful.
p.events
¶This is an instantiated instance of the Dispatcher
class. Essentially, it is the holding place for all custom events. It contains several methods that can be used to add, remove, and trigger custom events.
p.events.add(<NAME OF EVENT>,<CALLBACK>)
:<NAME OF EVENT>
as a potential event.p.events.remove(<NAME OF EVENT>,<CALLBACK>)
:<NAME OF EVENT>
as a potential event.p.events.trigger(<NAME OF EVENT>,<ARGS>)
:<NAME OF EVENT>
.var someFunction = function(arg1, arg2, arg3) {
...
}
p.events.add('myCustomEvent', someFunction)
p.events.trigger('myCustomEvent', arg1, arg2, arg3)
p.events.remove('myCustomEvent', someFunction)
p.stack
¶This is an instantiated instance of the DataStack class. It is used to hold state for the application. Data is kept inside the p.stack.storage variable. HOWEVER, it is strongly encouraged to NOT directly access the p.stack.storage object. Doing so will cause a lot of unexpected results.
Instead, you shoule interact with the stack by using the getter and setter methods.
p.stack.set(<NAME OF KEY>, <VALUE>, <OPTIONAL CALLBACK>)
:p.stack.get(<NAME OF KEY>, <OPTIONAL DEFAULT VALUE>)
:key
is not in the state
.p.stack.push(<NAME OF KEY>, <VALUE>, <OPTIONAL CALLBACK>)
:p.stack.update(<NAME OF KEY>, <NAME OF PROPERTY>, <VALUE>, <OPTIONAL CALLBACK>)
:Regardless of the key
, there will be an event emmitted when storing a key/value to the state
. Its name will be key + 'StackChange'
. Therefore, you can capture this by adding a custom event.
p.events.add('someKeyStackChange', function() {
console.log('This event was triggered by pushing an item to the stack')
console.log('The value of someKey is: ' + p.stack.get('someKey'))
})
p.stack.set('someKey', 'abcdefg')
This also works with the push()
method.
p.events.add('someKeyStackChange', function() {
var my_list = p.stack.get('someKey')
console.log('This event was triggered by pushing an item to the stack')
console.log('There are ' + my_list.length + ' items in somKey')
})
p.stack.push('someKey', 'abcdefg')
You can get all of the keys that have been set with the .keys()
method.
for (var key of p.stack.keys()) {
console.log(key)
}
Sometimes you do not want to override an entire object that is stored in the stack
. What if you only want to update a single property? No problem, call .update()
.
var nested = {
inner: {
property: {
found: {
here: 'Hello, world.'
}
}
}
}
p.stack.set('nested', nested)
p.stack.update('nested', 'inner.property.found.here', 123)
p.Request
¶This is an object used to make AJAX calls, and to return a response. To begin, it should be instantiated with a URL as its parameter.
var request = p.Request("http://example.com")
To actually make a call, you call either post()
or get()
on it. Both methods take data
as its first argument.
The data
variable can either be a url encoded string (foo=bar
) or an object ({'foo': 'bar'}
).
In addition, the post()
method also takes an additional second parameter: csrftoken
. This is a string that gets passed through to a X-CSRFToken
header. Right now, I know this is limited functionality that is screaming of some need for further abstraction. But, PorterJS was developed first and foremost to run with a Django backend, hence the csrftoken
in this form. Future releases will abstract away this logic, and also add better logic for adding headers.
To see this in action:
request.get().then(function (response) {
console.log(response.responseText)
}).catch(function (error) {
console.log(error)
})
PorterJS tries to do all the dirty work for you in trying to figure out what you want to happen. One way it operates is to use data-*
attributes on elements. Many other frameworks (we won’t mention which ones) spurn the HTML specifications and run custom attribute names. But not PorterJS. It is designed to work with HTML5 compliant source code. Here are all of the recognized attributes.
data-bind
¶<input>
and the DataStack.p.stack.get('someKey')
.<input data-bind="someKey">
data-class
¶See also .toggler
below.
<a href="#" data-class="classToBeToggled" class="toggler" data-target="someId">toggle link</a>
data-hash
¶<a data-hash="#go/to/here">Visit here</a>
data-method
¶GET
, except on a form
element that defaults to POST
GET
, POST
, PATCH
, PUT
, DELETE
<a href="/some/path" data-method="PATCH">link to call a patch</a>
data-model
¶<span data-model="someKey"></span>
or
<input data-model="someKey">
data-target
(when used on a .toggler
element)¶See also .toggler
below.
id
of the intended target.id
of the target.<a href="#" data-target="someId" class="toggler" data-class="classToBeToggled">toggle link</a>
data-target
(when used on an <input>
element)¶<form>
element to be submitted on submit.id
of the target form
.<form id="someFormId">
<input data-target="someFormId">
</form>
data-<EVENT NAME>
¶This can be used with any of the following events: click
, keyup
, keydown
, focus
, blur
, change
<input data-keyup="someValidator">
<script>
var someValidator = function (event) {
...
}
</script>
<a></a>
or [data-url]
¶By default, all <a></a>
tags will be captured to send HTTP requests asynchronously. However, you can opt out of this behavior with one of the following:
.exclude
or .ignore-self
[target]
In addition, you can create a “fake” link by setting [data-url]
on any element. This will attach a click even to it. This could be usefule, in the following example to make clickable table rows:
<table>
<tr data-url="/go/to/element/1">
...
</tr>
<tr data-url="/go/to/element/2">
...
</tr>
<tr data-url="/go/to/element/3">
...
</tr>
</table>
#content
¶…
.toggler
¶…
You can overide the default action by adding one of the following classes: ``ignore-self``, ``exclude``, or ``ignore-toggle``.
.modal-open
¶…
Web architecture is mainly centered around the request/response model. The browser sends a request to a server, that server returns a response to the browser. PorterJS is centered around this dynamic (and therefore is not currently built to handle websockets–COMING SOON!).
PorterJS expects a response from the browser to be in JSON format, and can accomodate any key/value structure it is given. However, to leverage its core functionality, there is a set of reserved key/value pairs that will trigger certain browser side actions.
breadcrumbs
¶COMING SOON
callbacks
¶{
'function': 'name_of_some_function',
'arguments': [
'list',
'of',
'some','
'arguments'
]
}
errors
¶COMING SOON
html
¶"string"
, {Object}
, or [Array]
"string"
, then the contents of that string will be inserted into an element with an id="content"
.{Object}
, then each element with an id that equals key
will have the corresponding value
inserted.[Array]
, then each item should itself be an {Object}
with two keys: id
and content
.// Scenario 1
{
"html": "Hello, world."
}
// Scenario 2
{
"html": {
"foo": "bar"
}
}
// Scenario 3
{
"html": [
{
"id": "spam",
"content": "eggs"
}
]
}
notifications
¶COMING SOON
redirect
¶window.location
location
.title
¶COMING SOON
Loaders are the tools that turn HTML events into events and building blocks. The process is initiated by the load()
method, which is also `publically available`_ <public.rst> as p.load()
. Whenever HTML is being rendered, it should be followed by this method to capture any newly inserted DOM elements. This should be
Anchors ``<a>``: By default all anchor elements will have their default behavior removed, and instead will trigger one of two options:
- If your `CONFIG`_ is setup for server side rendering, then it will trigger the
trigger_call
method. See `server side rendering`_ and `elements`_ for more details.- If your CONFIG is setup for client side rendering, then it will trigger the `router`_ .
You can opt out of this default behavior by adding ignore-self
, or exclude
as the element’s class.
NOTE: Make sure to exclude your element if you want to assign a different click event to it.
Any element with ``[data-url]``: By adding the data-url
attribute, any element can become a link. This is useful when you want an antire row of a table to be clickable.
There are two events that are emitted during the load()
. You can hook into it with your own functionality by adding an event for preLoaders
and postLoaders
.
p.events.add('preLoaders', function () {
console.log('Do something great./');
});
.. _router: http://
.. _elements: http://
.. _server side rendering: http://and `elements`_
.. _elements: elements.rst
.. _CONFIG: http://
Given any object, you can get or set a property in by a dotted string notation.
var nested = {
inner: {
property: {
found: {
here: 'Hello, world.'
}
}
}
}
console.log(nested.getProperty('inner.property.found.here'))
nested.setProperty('inner.property.found.here', 'Something else')