Leaf API
defineScope
defineScope is the most important API because it provides isolation between scopes, thus maintaining a clean code structure. DefineScope also creates the logic for your application’s UI.
Example
//scope_greeter.js
import { defineScope } from "https://cdn.jsdelivr.net/gh/Rahmad2830/Leaf@v1.0.0/dist/Leaf.min.js"
defineScope("greeter", () => {
//your code here
})
Context Object
Leaf have 2 main context object, first at defineScope level, second is in the function level.
Context in scope level
This context is in the defineScope params function.
//context placed in here
defineScope("any", (context) => {
//code goes here
})
This context scope has three main properties: root, which retrieves the element where the data-scope is defined. targets, which marks (retrieves) the element. values, which retrieves the value of other data-* elements within the root element.
You can also use object destruction to invoke the context. Example
//context placed in here
defineScope("any", ({ root, targets, values }) => {
//code goes here
})
- targets
Targets is useful for marking elements to be retrieved. You can mark multiple elements with the same target data using targets.all.[targets_name]. However, remember that using targets.all.[targets_name] will result in an array of elements. To process it, it must be looped first.
Example
<div data-scope="any">
<div data-target="hello">Something</div>
<p data-target="world">Something</p>
<div data-target="world">Something</div>
</div>
defineScope("any", ({ targets }) => {
const greet = targets.hello //take element with data-target="hello"
console.log(greet)
const allWorld = targets.all.world //take all element with data-target="world"
allWorld.forEach(element => console.log(element))
})
You can use all methods for elements. For example: greet.id to get the element ID, greet.hidden = true to set the element’s visibility, etc.
- root
root is useful for retrieving elements that have the data-scope attribute attached. Example: HTML
<div data-scope="any">
<!-- your code here -->
</div>
javascript
defineScope("any", ({ root }) => {
root.hidden = true
})
You can use all methods for elements. For example: root.id to get the element ID, root.hidden = true to set the element’s visibility, etc.
- values
Imagine if you needed other data in the scope, then you could put it in the data-* attribute. values is used to retrieve the value from the data- attribute in the scope element. If you write data-status=“pending”, you can call it with the prefix values.status.
Example:
HTML
<div data-scope="any" data-status="pending">
<!-- your code here -->
</div>
javascript
defineScope("any", ({ values }) => {
console.log(values.status)
//output "pending"
})
Always remember that values always return strings. If you pass a number, for example, data-id=“5,” then the number 5 will default to a string. To change this, you must manually change Number(values.id)
Context in function level
The context object at the function level is located in the function parameters that you create in defineScope.
Example:
defineScope("any", () => {
//context in function level placed here
function greet(context) {
console.log(context)
}
})
You can also use object destruction to call this context. Example:
defineScope("any", () => {
//context in function level placed here
function greet({ element, event, params }) {
//do something
}
})
The context at this level also has three main properties: element, used to select the element to which this function is attached. event, which is the event in a regular listener (e.g., event.target.closest). params used to retrieve data-* values within this element. Since the element to which this function is attached must be a data-action, it is more accurately called an action context.
- element
element is useful for selecting elements that have functions attached to them, in this case elements that have data-action attached to them. Maybe you need element.id or something similar, then this will be very useful.
Example:
<div data-scope="any">
<div data-target="hello">Something</div>
<button data-action="click->toggle">hide</button>
</div>
defineScope("any", ({ targets }) => {
const hello = targets.hello
function toggle({ element }) {
hello.hidden = !hello.hidden
//set the button inner text
element.textContent = hello.hidden === true ? "show" : "hide"
}
return { toggle }
})
The code above will change the inner text of the button to show or hide according to the condition of the hello element.
- event
context event does not need to be explained in too much detail, it is the same as the event in the listener parameter.
Example:
<div data-scope="any">
<label for="name">Your name</label>
<input type="text" name="name" id="name" data-action="input->run" />
<div data-target="output"></div>
</div>
defineScope("any", ({ targets }) => {
const output = targets.output
function run({ event }) {
output.textContent = event.target.value
}
return { run }
})
The code above will do something like two-way binding, where when you write your name in the input, the output will be updated automatically.
- params
params is used to retrieve the value from data-* within an element that has a data-action. Think of it like a function parameter. To retrieve it, use the prefix params.[data-name]. For example, if you’re looking for the ID data-id="5", you can retrieve it using params.id.
Example:
<div data-scope="any">
<button data-action="click->getUser" data-id="5">hide</button>
</div>
defineScope("any", () => {
function getUser({ params }) {
console.log(Number(params.id))
//output "5" (string)
}
})
Note that the resulting value of params.id is a string, and will always be a string. You must manually change it to Number(params.id) as well as the boolean value.