bigskysoftware / _hyperscript Goto Github PK
View Code? Open in Web Editor NEWa small scripting language for the web
License: BSD 2-Clause "Simplified" License
a small scripting language for the web
License: BSD 2-Clause "Simplified" License
Is it possible to add pattern matching and switch-like statement? To make it more natural it can be like:
when param1 and param2 is like 'abc*' and 'xyz*' ... when param3 is ...
I'm kicking around a number of updates to the fetch
syntax, while still trying to keep things as short and expressive as possible. Can we use this ticket to start a discussion on the best way to make improvements?
One thing that came up is the ability to use HTTP verbs as the specific command: enabling something like this:
get [from] <server-url> [using <headerValue>]
post [<object>] [into] <server-url>
put [<object>] [into] <server-url>
delete [<object>] [from] <server-url>
fetch <server-url> -- could still work as a "GET" request
I think this would bring HTTP verbs to the API surface with little or no cost to the language syntax. It also fits the spirit of hyperscript as "a scripting language designed for the web and inspired by HyperTalk." The one red flag is that put
is already in use as a (virtual) synonym for set
.
If we decide that we like this syntax, it would be a breaking change that we would need to manage carefully. The first step would probably be to confirm that set
is able to accomplish everything that put
does currently, and notify developers of the upcoming change.
Though it might be possible to enumerate some of the common header values, I think the cost probably outweighs the benefits.
A statement like get from /my-url using authorization "12345" and cache-control "no-cache"
is really cool, but unwieldy in practice. If I need to send complicated headers to the server, it's more likely that I'll want to manage them like this
set commonHTTPHeaders to {
"Authorization: "12345",
"Cache-Control": "no-cache"
}
get from /my-url using commonHTTPHeaders
Add a default function into the hyperscript configuration object -- similar to the one in htmx -- that would let developers override the current behavior. For example, I wrote an htmx extension to save authorization headers supplied in any http response, then add them to every subsequent http request. I'd like to make the same feature available in hyperscript as well.
These custom function signatures might look something like this
var config = {
// process request before sending via hyperscript
beforeFetch: function(url, options:Object): Obje t {
return {url:url, options:options};
},
// actually does the fetch command
fetch: function(url:string, options:Object): Promise<Object> {
return fetch(url, options);
},
// process response object before returning to hyperscript
afterFetch: function(response:Object): Object {
return response;
}
}
I'm using alpine with htmx/hyperscript.
I noticed that if I add htmx via the template tag the components are not initialized.
E.g.
<template x-if="popup === 'signup'">
... HTMX ...
</template>
It is important to init HTMX/Hyperscript via
htmx.process(addedTemplateTag);
_hyperscript.processNode(addedTemplateTag);
It would be nice to have this documented for future users. Also is it possible to reduce both process calls into one?
One suggestion could be for html to check if _hyperscript is present and call processNode.
Thank you
I'm trying to remove the disabled
attribute following the example in DOCs but this very basic code does not work (console reports a number of errors).
<button id="btn0" type="button"
_="on click remove [disabled] from #btn1">
Disable MyButton
</button>
<button id="btn1" type="button" disabled>
MyButton
</button>
Without going overboard and re-developing something like Moment.js, I think Hyperscript could benefit from a few time/date commands. It could easily be wrapped into its own separate package so that it doesn't affect people who don't need it.
Tag | Result | Example |
---|---|---|
today | dateTime value of current day | |
tomorrow | dateTime value of next day | |
yesterday | dateTime value of previous day | |
now | dateTime value of current time | |
before, after | modifies a dateTIme value | 1 day before today |
from | same as after |
1 year from now |
year, month, week, day, hour, minute, second | date range for | 3 weeks after today\ |
is before, is after | date comparison function | if endDate is before 1 year from now then log endDate |
This code
<img src="image-url" _="on load log me.src" >
create two console log entry with same "image-url"
I suppose because is intercepted "load" node event and "load" image specific event.
Is there any way to distinguish the two events?
In addition if I use
<img src="" onload="myFunction(this)">
and programmatically I set src to image-url, myFunction is correctly executed after remote image-url is loaded
if I use
<img src="" onload="myFunction(this)"_="on click log me">
and I don't set src programmatically, myFunction is always executed on page load
I noticed this gives me undefined
:
log #element[attr-name]
as does log #element.attr-name
and log attr-name of #element
. What is the correct way to read an attribute value?
Trying to follow the docs and do:
<div _="on htmx:load log 'Loaded!'"></div>
And getting:
Uncaught Error: Unexpected Token : :
on htmx:load log 'Loaded!'
^^
at Object.raiseParseError (_hyperscript.js?compile=false:395)
at _hyperscript.js?compile=false:1153
at parseElement (_hyperscript.js?compile=false:402)
at Object.parseHyperScript (_hyperscript.js?compile=false:416)
at Object.initElement (_hyperscript.js?compile=false:568)
at init (_hyperscript.js?compile=false:1665)
at _hyperscript.js?compile=false:1653
at Object.forEach (_hyperscript.js?compile=false:478)
at HTMLDocument.fn (_hyperscript.js?compile=false:1652)
I'm using the latest version of both HTMX and hyperscript.
What am I missing here?
Now that hyperscript has behaviors, it could be useful to support using variables within expressions to make them more configurable. For example.
<div data-hidden-class="hide"></div>
And then be able to use the defined hidden-cass
within hs code.
set `.${@data-hidden-class}`
I stumbled into this while working on SSE Support (#52). I've now exposed functions like call MyConnection.start()
and call MyConnection.stop()
to the rest of hyperscript. This works fine, but it would be better if I could say something likestart MyConnection
and stop MyConnection
instead.
We have now added several kinds of background services: Workers, WebSockets, and SSE that could all use this kind of syntax. And it seems likely there could be others in the future that could implement this, too.
Interfaces
I think it could be implemented using something like a duck-typed interface, so the start
keyword would take a single argument -- the name of the object in the hyperscript context -- and try to call the .start()
method on that object. If the method doesn't exist, then hyperscript could log an error.
The stop
keyword would do the same thing, searching for a .stop()
method instead.
Naming
These names are not set in stone -- there are many other pairs that might work instead (connect/disconnect, open/close, etc) -- but these seem like the most generic terms that would be applicable in the widest range of circumstances.
Right now we parse expression and then check the type, but this screws w/ grammar in some places. Should just parseAny()
Using template literal values for set fails and the browser hangs
<input type="text" id="txtName"/>
<button type="button" _="on click set message to `hello ${#txtName.value}` then call alert(message)">Say Hello</button>
#worker
, should probably be #workers
.How can an attribute of the type data-* be added to an element?
If I use
on click add [data-initial='name']
I get this error
Expected ']' but found 'data-initial'
Is it possible to compose/encapsulate commands? it'd be nice to be able to do something like _="on blur validate-email me"
rather than writing out a whole regex every time, especially if some email inputs have additional logic.
HyperTalk used play
to trigger audio files. Hyperscript could use this for audio (or possibly video) files as well. This also sets a local variable called "the sound" that could be used to reference the audio after.
on click
play /sounds/pop.mp3
on mousedown from AngerButton
repeat until event mouseup
play /sounds/klaxon.mp3
end
stop() the sound
There is an error in console:
VM59:10 Uncaught TypeError: Cannot read property 'applyEventListeners' of undefined
at Object.applyEventListenersTo (eval at initElement (_hyperscript.js:638), <anonymous>:10:66)
at initElement (_hyperscript.js:639)
at _hyperscript.js:621
at forEach (_hyperscript.js:525)
at Object.processNode (_hyperscript.js:620)
at processNode (_hyperscript.js:1736)
at HTMLDocument.<anonymous> (_hyperscript.js:1778)
applyEventListenersTo @ VM59:10
initElement @ _hyperscript.js:639
(anonymous) @ _hyperscript.js:621
forEach @ _hyperscript.js:525
processNode @ _hyperscript.js:620
processNode @ _hyperscript.js:1736
(anonymous) @ _hyperscript.js:1778
I had to change my event name when I failed to use cart-updated
or "cart-updated"
in a piece of hyperscript
code.
Trying to use: _="on click ajax GET /main then put response into #main". This simply does not work? Doesn't seem to like the use of #main.
Updated with customized JS code called upon onsubmit
.
Playing with an example for bigskysoftware/htmx#398 , I am trying to trigger a submit
event when hx-post
is pressed.
<!DOCTYPE html>
<html lang="en">
<body>
<form action="{{ request.path }}" method='post' id="myform"
onsubmit="return validateForm()">
<label for="choose">Would you prefer a banana or cherry?</label>
<input id="choose" name="i_like" required>
<button>Submit</button>
<a href='#' _="on click send submit to #myform" hx-post="{{ request.path }}">hx-post</a>
</form>
</body>
function validateForm() {
var x = document.getElementById("choose").value;
if (x != "apple") {
return false;
}
}
</script>
<script src="https://unpkg.com/[email protected]"></script>
<script src="https://unpkg.com/[email protected]"></script>
</html>
It appears that the form
element did receive the submit
event but hx-post
did not wait for the completion of the submit
event (result of validateForm
) and went ahead with AJAX post. Is there anyway to wait for the completion of submit
, cancel hx-post
ifvalidateForm
returns false
?
Hi, i am trying to do _="on redirect(url) log 'url is: ' + url then ajax GET '' + url then put response into test.innerHTML">
and it errors on '' + url
My server returns a custom response header: HX-Trigger: {"redirect":{"url":"/hello"}}
I also tried _="on redirect(url) log 'url is: ' + url then ajax GET url then put response into test.innerHTML">
, but it tries to ajax to /mypath/url instead of going to /hello
If i did _="on redirect(url) log 'url is: ' + url then ajax GET /hello then put response into test.innerHTML">
then it works, but i'd like to avoid any hardcode in the command.
Any pointers ? Thank you ..
Getting this error as soon as I add _hyperscript.
The error replicates on every click.
Trying to use the following _="on click remove .hideme to #email-form" and it throws this error: Uncaught Error: Unexpected Token : to this is basically the same as one of the examples? Whats wrong with this?
[updated] Found that in another example it should have been from instead of to - however it would not work with #email-form id - had to add a class named email-form and then use .email-form as the from selector.
Would be nice if something like on click remove .hidden and .inline-block
or on click remove .hidden .inline-block
was possible.
Really interesting project!
Could you clarify the relationship with Htmx? I see you call it a companion project but I'm not entirely sure what that means.
This would help people like myself who are interested in both but want to understand where they are headed before committing time to one.
Could someone kindly answer a question I posted on Stackoverflow asking if I can use variables with the add
command?
For example:
without variable (id = #1022):
<button _="on click add .green to #1022" type="button">
Click
</button>
with variable (id = my_variable):
<button _="on click add .green to my_variable" type="button">
Click
</button>
and then something like this:
<script type="text/javascript">
function setID() {
// here the code to get the ID and set my_variable
return id;
}
</script>
The original HyperTalk used &
for string concatenation, and &&
for string concatenation with a space in between. This would be really useful, and would help avoid statements like this set fullName to firstName + " " + lastName
-- changing them into this instead set fullName to firstName && lastName
Right now, hyperscript uses &&
as a logical AND operator, and we should not change this. But, would it be possible (and not too confusing) to overload this behavior when dealing with two strings? Or, would it be better to use a different operator for this?
Can you add show
and hide
https://hyperscript.org/docs is a 404
I think I just made my first operator for hyperscript (yay!). To flatten the learning curve, it would be very helpful to document the basic parameters required by the internal functions in hyperscript.
I don't know if it's best on the website, somewhere in a GitHub wiki, or as comments in the source code, but here are the functions/parameters that I'd love to understand better:
_runtime.unifiedEval(parseElement, op, ctx)
parser.parseElement(type, tokens, root)
parser.addGrammarElement(name, definition)
definition(parser, tokens)
function passed in to addGrammarElement
definition(parser, tokens)
expression.evaluate()
and when must I call _runtime.unifiedEval()
Is it possible to support parameters like event with param1, param2 ...
Hi, i did ajax using _hyperscript, and i notice the htmx request headers are not there yet.
Is this intentional or perhaps there is a way to get the headers sent ?
Thank you ...
I love the new measure command, but I'm running into trouble using it. Here's my code:
def center(node)
with node measure me then log it end -- works
measure node then log it -- doesn't work.
end
I want to make a function that takes an HTML Element and moves it around the screen. The first statement with node...
works fine, and I'm able to get the measurement of the Element. But the second statement returns an error "Uncaught (in promise) No such measurement as node". I think this is because it does not recognize the variable as a node, and assumes that it is a (missing) dimension that it's supposed to find.
So, perhaps we need to update the syntax that measure
is using?
Also, being able to pull specific measurements into variables is nice, but it's no use if I have to measure two items (like the window and the HTML Element I'm going to move). The best I've come up with is the rather clunky
measure #sourceNode then put it into sourceMeasurement
measure #targetNode then put it into targetMeasurement
... then use the two new variables below
How much flexibility do we have to tweak this command?
Hi,
I was playing with behaviors in [email protected], and I'm not sure what the expected way to write it is. Here's what I've tried.
If I add it into a script tag with text/hyperscript
, and install it on an element, I get a No such behavior defined as Removable Error: No such behavior defined as Removable
(e.g. using the Removable example in the docs at https://hyperscript.org/docs/#behaviors).
But if I add the behavior in the hyperscript attribute of an element, and add install Removable
it in any element after, it works?
The wait
syntax in hyperscript does a great job of enabling event controlled flow. However, it has trouble when waiting on more than one event at the same time. For example:
on closeModal(event)
add .closing to #modal
async do
if theURL is not null then
fetch theURL then
put it into #main.innerHTML
end
trigger fetchDone
end
wait for animationend
wait for fetchDone
remove #modal
end
This should work if animationend
finishes before fetchDone
, but it deadlocks if fetchDone
completes first.
I think hyperscript would benefit from a wait for x and y
syntax that is similar to Promise.all()
that only returns after all of the events have triggered.
This might be generalized to other kinds of operations, such as wait for any of x and y and z
but my first guess is that this would be less commonly useful.
I create a JSFiddle to reproduce the issue
I used the syntax from the example page
<div _="on load wait 5s then remove me">Here is a temporary message!</div>
I was recently working with a string comparison and I struggled to guarantee that an object was not null
. While I eventually worked it out as not(no value)
I think we could make hyperscript more readable with another pair of single argument operators: is empty
and is not empty
.
My first thought is that is empty
should return true
for all values that are null
, undefined
, [empty string], or zero length Objects or ArrayLike values. But if someone else has a better definition, I'm open to whatever makes the most sense for the community. The is not empty
operator would obviously return the opposite of is empty
.
I'll happily take a shot at building this (along with tests and documentation) if I can get a hoo-ah from @1cg
There's an amazing javascript library called HotKeys that makes it stupid easy to build keyboard shortcuts. Hyperscript does this very well, but I think that we could make it even better by borrowing a couple of easy-to-implement ideas from Hotkeys.
We wouldn't need to change our event handlers, just enhance the events with some extra data that makes it easy for both humans and machines to recognize the keyboard combination being pressed:
on keypress(key)
if key is "ctrl+s" return save() end
if key is "ctrl+f" return find() end
if key is "alt+q" return somethingElse() end
if key is "ctrl+alt+M" return anotherCombo() end -- one way of handling uppercase characters.
if key is "ctrl+alt+shift+m" return alt() end -- an alternate way of handling uppercase characters to consider.
This would really just require an extra function that looks at keyboard events, generates this string, then inserts it into the event details. Yes?
Hi, is it possible to extract some part of the response from ajax
command?
For now I'd need to do something like this:
on click ajax GET /my-data then put response(#content) before me end
This is of course just to give you an idea of what I would use. Important thing here to remember is that the response could be json or any other format, so the language should address that somehow.
Alternatively it could just be left to custom JS function that would extract the value.
HyperTalk used identifiers like field
and button
to locate screen elements for later use, using a string to match the element ID or NAME. I see several benefits to adding this:
name=""
<#${dude}/>
set field "MyField" to "123"
send click to button "MyButton"
add .className to tag "MyTag"
This syntax would work great with variables that name the field/button/tag instead of hard-coding tag names
set field VariableThatContainsAFieldName to "boo-ya"
tell novelist SelectorVariable remove me
Identifier | Result |
---|---|
tag | returns the first tag whose ID that matches the provided string |
button | returns the first button whose ID (or NAME?) matches the provided string |
field | returns the first input, select-box, or textarea whose ID or NAME matches the provided string (special handling for checkboxes and radio buttons |
nodelist | returns a NodeList (array) of elements that match the provided selector. (Similar to < /> but works with a string variable |
Tried to do on load call auto_select()
. This crashes with a vague message about _
. If I rename the function to autoSelect
it crashes if I call it with zero parameters like that.
Still haven't gotten it to work with those two changes though...
This code working with version 0.0.7 no longer works when upgrading to version 0.0.9
<button class="btn btn-primary d-none cropped-only" hx-post="..."
_="on htmx:afterRequest get detail.xhr.response.includes('success') then
if it with <.avatar img/> set me.src to zu.imageCropper.objectURL then remove .d-none from me end"
>Confirm</button>
I tried replacing "with" with "tell" but "me" always refers to the "button" and not all occurrences of ".avatar img" on the page.
Am I missing something?
Right now you can only destructure properties from the details
property of an event into variables. This is fine for custom events, but does not work for properties directly on the event.
This logic:
_hyperscript/src/_hyperscript.js
Line 1275 in 064004b
should check both places, when setting the value of a destructured variable.
I think hyperscript would really benefit from a map
command that takes an array and maps it to a series of values. We originally discussed this as a property of arrays, but I think it would work easier as a command on its own -- perhaps bundled into a separate library of other similar commands. Here's how it might work
Map takes a variable name (which must be an array) and a result template and returns a new array with values mapped from their original values into the result. It would work very similar to the built-in array.map
function, without the lambda calculus. The syntax would look something like map <variable> (to|into) <expression>
When evaluating the result template for each item in the array, these variable names would be available for use:
index
: the current index number of the item in the array
value
: the value in the array at that particular location
Examples
fetch /my-server
map it into {}
This could also work with template literals
fetch /my-server as json
map it to
`<tr>
<td>Record #${index + 1}</td>
<td>${value.firstName}</td>
<td>${value.lastName}</td>
<td>${value.emailAddress}</td>
</tr>`
put it into #my-table
If this syntax is acceptable, I'm happy to try submitting a PR. If I can figure out how to do it, I'll probably propose match (all|first) from <variable> (on|with) <expression>
next :)
Hey there,
There's an error in the following example page: https://hyperscript.org/commands/remove/
<div _="on click remove .not-clacked to #another-div">Remove Class From Another Div!</div>
I think the to
should be from
, so:
<div _="on click remove .not-clacked from #another-div">Remove Class From Another Div!</div>
Thanks!
Doing something like add [my-attribute='value']
fails with an error:
Uncaught Error: Expected ']' but found '-'
on htmx:beforeRequest add [my-attribute='value']
^^
Something like:
<button _="on click wait #input.value then toggle .clicked on me">Click</button>
Does hyperscript need a global
keyword? HyperTalk had one, and it feels dirty to use window.variableName
to define a value that's available everywhere.
init
set global greeting to "yo"
on click from button "myButton"
set greeting to "mama"
Basically:
<a _="on click toggle between .this-class and .that-class on #my-element">Click</a>
<div id="my-element" class="this-class">
When you click the <a>
, it should remove this-class
and add that-class
, or vice versa, depending on the initial state.
Alternative syntax, maybe: on click toggle between .this-class, .that-class on #my-element
but I think the 'and' works better here.
Very helpful for a broad range of UX patterns.
From discord:
server sse://whatever
on foo
-- additional commands. "it" is populated with the event?
end
on message
-- more commands
end
on unhandled
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.