Public JavaScript API #39

Closed
bartaz opened this Issue Jan 8, 2012 · 47 comments

Projects

None yet
@bartaz
impress.js member

impress.js should provide public API with methods that will allow triggering transitions between slides.

Proposed API methods:

  • next()
  • prev()
  • goTo( step )

If you think any other method may be useful, just let me know in comments.

@shama
  • beforeChange() callback
  • afterChange() callback
  • canvas({ css }) to directly manipulate the canvas
  • slide( id, { css } ) to manipulate a slide
@bartaz
impress.js member

By { css } you mean the data that describes position of the slide / canvas?

@richardhodgson

I use impress.js for a project of mine, however there wasn't a way to control the transition duration or easing function. My project also loads JS using RequireJS.

I forked impress, added support for AMD module format and the ability to pass in config. Original demo should work fine, there's also a different example showing using the API.

Happy to contribute this back if its of any use.

https://github.com/richardhodgson/impress.js/blob/api/examples/config.html#L88
richardhodgson/impress.js@master...api

(another vote for before/afterChange events too)

@shama

@bartaz Yes. Although now understanding impress is only for presentations my suggestions are likely not so useful. As a presenter wouldn't need to adjust the canvas or a slide during the presentation. I just like how you handled prefixing and thought it would be useful to provide access.

I'll throw a vote for more callbacks though:

  • onStart() presentation starts (called once)
  • onEnd() presentation ends (called once)
  • onFirst() hit the first slide
  • onLast() hit the last slide
@bartaz
impress.js member

@richardhodgson I was thinking about using AMD when starting this project, but it seemed so simple that I thought it wont be needed. But as there seems to be a need for API I'll consider it again.

I still don't want to require require to use API, so will probably expose it as global namespace instead (if define is not available), the way jQuery does it.

@richardhodgson

@bartaz jQuery way makes sense - pleases both groups :)

How do you feel about impress being less static, more instance based?

var pres1 = new Impress('elementId');
var pres2 = new Impress('someOtherElementId');

pres1.goTo(1);
pres2.onStart(myCallback); //etc
@bartaz
impress.js member

I don't really see the point of having more than one instance of impress object, it's really supposed to sit in the middle of viewport and simply run a presentation on keypresses.

What would be the sense of having more impress in one document?

I'd also like to avoid the necessity of having the 'initialization' code. That's why I tend to keep config in data- attributes, so the script can just be included, self execute, and that's it.

@richardhodgson

@bartaz Libs that automatically invoke themselves as soon as they are loaded can be harder to incorporate into other projects. However, I get that the focus of impress is something simple that is driven by the content of your presentation.

How would more global options be specified in the DOM? A data- attribute on the container div?

<div id="impress" data-transition-duration="0.5">
    ...
</div>
@bartaz
impress.js member

@richardhodgson Exactly. I'd like to keep this simplicity. Although I understand that when the number of possible options grows, it would be more 'natural' to set their values with JSON object passed to some initialization function than to define dozen of data- attributes on some DOM element.

I'm thinking if it would be possible in any way to keep impress.js self-running by default, but being able to tell it not to run and wait for initialization from script...

@naugtur

After working on an WYSIWYG editor I have a few thoughts on useful API functions. Any editor would benefit from (ok, require :) ) revealing the function that builds slides (the one inside slides.forEach) and a function to turn off and on handling events (mostly mouse). It doesn't have to remove bindings, it would just make impress return in the first line of the event callback

impress.draw(DOMNode/DOMNodeArray)
impress.handleMouse(true/false)

@presidento

Do not use callbacks! Use the observer pattern, like MicroEvents.js's bind or EventEmitter's addListener and on, or what is in DOM: addEventListener. With this pattern more than one callback can be added. (My favorite method name is the addListener.)

Edit: JS API will be great! A lot of feature request can be implemented outside impress.js. (For example: using mouse wheel to change the page, or go to overview with the Home key, or mark some slides as favorite slide and access it with numeric keys, etc.) Thank you for your great work!

@thingsinjars

I've been working with a modified version with a public API in my fork and found I needed next, update and target. update is simply the bit that updates the CSS of the given element (the same bit @naugtur refers to above, in fact).

Of course, most might not need update or target, it's specifically for edit mode in my case.

Also, yeah, don't use callbacks if you can help it.

@naugtur

@fmate14 If there is an API you can always wrap the API in any publish and subscribe functions you want in less than 2*n+2 lines of code ( n being number of API methods ). Why would anyone add a dependency on a pub/sub implementation to a project or pollute the global space with lib-specific events?

@presidento

@naugtur: do not add dependency, use an approved pattern.

I have shown only some examples of this pattern. If you look into the MicroEvents.js at line 14-30, you can see that implement this pattern requires only a few code. (Although I don't like the trigger/bind method name, I think, addListener or on and fireEvent or emit is better, but this is not an important decision. And the unbind method is perhaps unnecessary.)

@naugtur

@fmate14 I don't want to create an offtopic here, so it's my last comment regarding this. I just wish to explain. An API is a nice and contained solution and (as you pointed out) not much code is needed to implement it with events. Any built-in implementation of pub/sub forces users to use this particular one. If I already have a different implementation in my project using two is absurd. The pattern is obviously a great thing but you can't force it on people.

@thingsinjars What is your target method? It's a bit hard to guess judging only from the name.

@thingsinjars

@naugtur Oh yeah, I should've said. It's basically the functionality used to identify the target within the mousedown listener. It takes in the event and returns the relevant node.

@naugtur

@thingsinjars That's a JS feature. if you have the event you jus type event.target and it's there

@thingsinjars

@naugtur Not in this case. It uses event.target but works upwards to find the relevant parent slide. This functionality is aware of the structure of the slides. If this were to change, the method required to identify target slide from event target would change, hence abstracting as an external API call

@naugtur

@thingsinjars It's a matter of binding an event to a slide, not anywhere inside

@thingsinjars

@naugtur Yes, but target handles (in a black-box way) detecting the document.click and returning the relevant slide. This is not about me being able to attach events to slides or not, it's about integrating with the functionality to ensure that a mouse click on the document is reliably associated with the same object whether the detection is done inside or outside impress.js.

We can move this discussion to a different thread to stay on-topic here. I was just passing on info about a piece of internal functionality I found useful externally which could, therefore, be considered for API access.

This was referenced Jan 18, 2012
@medikoo

@bartaz can you share a bit more about your idea of that API ?
Do you plan to expose impress to global namespace (under what name ? IMPRESS ?)
I have some simple proposals already implemented and they rely on already exposed impress. I'm holding pull request now as I don't want to conflict that with your vision.

@bartaz
impress.js member

@medikoo Global namespace/object for sure. Names "impress", but I'm still not sure about the case (yes, I know it seems to be a small problem to worry about). It won't be Impress cause it's not a constructor function (or maybe it should be?), IMPRESS namespace looks kind of weird, so maybe simple global object impress will be best choice.

Whatever it would be, all the API functions will be exposed there.

@medikoo

Good point, all caps is rather used for global object that holds many different namespaces, e.g. LIB, in that case impress is better choice.

@commadelimited

I'd just like to see something as simple as an onchange event. Fire that event when the slide loads so that I can trigger custom js on a slide by slide basis.

@bartaz
impress.js member

Hi,

I would like to move impress.js forward with small steps, so in here let's focus on providing simple public API to control presentation and events feature will be handled separately in #86. So please move any events discussion and suggestions there.

@bartaz
impress.js member

Talking about API.

I've started to build something and made it available in api branch. Please have a look and let me know what you think.

It basically introduces global impress() "factory" function that initializes impress presentation and returns the API to control it. It may seem weird to make it a function instead of an object or namespace, but I'm planning to extend it and allow calling it with custom configuration object to override default settings.

So it doesn't initialize automatically anymore, you have to call:

impress();

To externally control the presentation use this function to get API object. Presentation wont get initialized more than once, don't worry:

impress().selectNext();

Or you can store api for later use

var api = impress();
api.select( document.getElementById("overview") );

Currently available API functions:

  • select( element ) - selects given presentation step (only if given element is a presentation step)
  • selectNext() - selects next step
  • selectPrev() - selects previous step
  • isStep( element ) - checks if given element is a presentation step

Try it out and let me know what you think about such approach. I'd like to push it into master as soon as possible.

@medikoo

In general it's great, I would just do method names less verbose:

  • next()
  • previous()
  • goto(number | id | element) - seems semantically more correct to me.

What's the use case for isStep?

@bartaz
impress.js member

I agree about the names (right now they are just copies of internal functions' names - that could be renamed too for clarity btw). And the additional input options for goto (number or id) are also part of my plan ;)

When it comes to isStep it's used for example in click handler to find the closest step element of the target. It probably wont be needed widely, but I'd like to extract navigation handlers such as keyboard/mouse/touch out of the core, so finding step element may be useful. And having such function is safer than just checking the class name .step cause this way you are sure it's an element initialized as step by impress.

@medikoo

@bartaz :) it makes sense, but then maybe something like getStep(element) would be better ?

@bartaz
impress.js member

@medikoo and it would return the closest parent that is a step?

@medikoo

@bartaz yes, I see it as DOM function and I think it would be more convenient than isStep. If we would name it verbose DOM way it would be getStepByElement, so getStep is probably best name :)

@thingsinjars

I agree with the name (and functionality) of getStep. In my fork, that's exactly the bit exposed as target but getStep is a much better name.

@bartaz
impress.js member

I updated the api branch. Following the suggestions now we have:

  • next()
  • prev()
  • goto( el ) - not accepting numbers or ids, yet
  • getStep( el ) - returning the parent step for any DOM element, not sure if the name is clear enough, but I'd like to avoid making it too long

Let me know what you think. I kinda like how it ended up.

@medikoo

Looks great to me :)

@bartaz
impress.js member

Basic API released in 0.3

For suggestions about event to be triggered by impress join issue #86

For suggestions about additional API methods for authoring tools join issue #88

@bartaz bartaz closed this Feb 19, 2012
@mariusa

Pardon my ignorance. I've tried to use goto() without success, by passing to it either $('#bored').get() or other variations.

How can it be used to jump to a div with id=bored ?

@bartaz
impress.js member

I don't know where you test it (demo site doesn't have jQuery).

Anyway, you need to pass argument to get function $("#bored").get(0) or $("#bored")[0] and it should work fine:

impress().goto( $("bored")[0] );
impress().goto( document.getElementById('bored') );
@mariusa

Hi

It does work when used at the toplevel of the doc, but not when inside a function handling onclick. e.g.

I've created here the testcase above. When clicking on the 1st slide, it shows 'clicked' in console but it doesn't jump to title.
https://gist.github.com/1977671

@bartaz
impress.js member

The code actually works, but impress.js itself binds click event to step elements, so if you click the step impress goes to this step.

So basically your code first goes to 'title' step, but then immediately goes to 'bored' one.

You can just find the click event in impress.js and remove it. It starts around line 394.

// delegated handler for clicking on step elements
document.addEventListener("click", function ( event ) {
@sokra

bartz is correct.

you can also use methods which prevents the other click handlers from being executed:

http://api.jquery.com/event.stopPropagation/
http://api.jquery.com/event.stopImmediatePropagation/
http://api.jquery.com/event.preventDefault/

@bartaz
impress.js member

yes, that's even simpler, just add stopPropagation in your handler

$('#bored').bind('click', function(event) { 
    console.log('clicked');
    impress().goto( document.getElementById('title') )
    event.stopPropagation();
})
@mariusa

Ah, got it. Makes sense now. Thanks.

I have the following presentation type: a 'step' with a list of items, and clicking on each should go to a specific detail 'step'. However, next step after a detail step should be the list again.

Therefore, it would be nice for impress to support "data-next", "data-prev" attributes for steps, similar to "data-x", so one doesn't have to use the api for these simple flows.

@bartaz
impress.js member

If you have a list of steps you link to, you can simply use links with id:

<a href="#title">Title step</a>

It will change the url and impress will detect it and show given step. No additional JavaScript needed.

@mariusa

No, I navigate with keyboard arrows

@sokra

there was a pull request for that #61 but i don't think it will merge anymore.
Consider https://github.com/shama/jmpress.js for such a advanced use case (sorry for advertising @bartaz )
http://shama.github.com/jmpress.js/docs/#route

@MapeWave

Hi..i need some help .. In my impress.js presentation i'd like to create a connection between two specified steps, the point is i want that ONLY when i'm in the 3rd slice(step-3) just pressing 'Return'(keycode=13) on my keyboard i go straight to the 6th one(step-6). What i've done till now allows me to do it..yeah..but during the whole presentation! while i want it working just on the third step..i know that something is missing in this part of code i wrote so i'll really appreciate if someone could have a look :

document.addEventListener("keyup", function ( event ) {
if ( event.keyCode === 13 ) {
switch( event.keyCode ) {
case 13: // invio-enter(13)
api.goto("step-6");

@39dotyt

goto should have a callback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment