jQuery inspired selection and operations

There is a lot of stuff I did in famous-views which I felt I should have really happened at a lower level (maybe above engine, but definitely relevant for all integrations). One of those was jQuery style selectors. I think jQuery was such a big hit because it made working with DOM Nodes such a pleasure. It’s basic usage is also very familiar with - I think - a majority of developers.

In famous-views you can currently give Nodes a unique id or any number of class's and do stuff like:

  • FV(node)- get an fview instance of an existing ref
  • FV('#id') - get a specific node
  • FV('.class') - get all nodes with that class
  • FV('Node') - e.g. get all nodes (multiple selectors were planned for the future)

And then using chainable methods like FV(fview).children(selector), closest(), parents(), parent(), find(), siblings(), eq(), each(), you could do stuff like:

var deck = FV("#cardDeck");
deck.doSomethingCool(); // plugin architecture
deck.children('.selected').bounceAnimation();

// Or the second part in one command
FV('#cardDeck > .selected').bounceAnimation();

Very basic examples but I think everyone gets the idea. Just having functional methods for working around a tree is super helpful!

The only downside of course is that where possible, direct (non selector) methods are always preferable for performance. But of course, scene graph in JS is significantly faster than DOM and DOM selector queries. I think it’s a pattern that could help adoption with caveats mentioned in a “Performance Optimizations” post. And only relevant for building components, not for internal use.

cc: @peacechen, IjzerenHein

2 Likes

What about implementing said query interface as a plugin?

That way people could use it if they want it, and UI widget designers could list it as a dependency on their respective plugins.

3 Likes

This would be great to have!! Famous wanted to work with jQuery… too bad they didn’t think of this. This is exactly what would makea jQuery developer happy IMHO.

2 Likes

:+1: Yeah!

… 20 chars.

I changed the minimum message size to 1 char. :smiley:

2 Likes

I’m not a fan of this approach. Queries for nodes on a scene or components attached to them should just be methods on the Scene.

‘Scene.find(’#component’)’ is simple enough. The API doesn’t need to mimic jQuery. If someone wants to build a widget with jQuery UI then this would be appropriate there.

As mentioned by @oldschooljarvis, I believe this would be fine as a plugin. It’s not completely “rendering engine” related, so it makes sense as a plugin, and it would be useful. I also agree that it can be a API on Scene or on the engine instance.

Hi all. I’ve been reading to get caught up, so if what I say sounds strange it’s probably because I haven’t quite succeeded. Here goes:

TL;DR - DOM manipulation via jQuery is not needed. If we follow the React design pattern all DOM-related stuff, including the state of the DOM elements, can and should be coded and stored in Javascript. It will lead to both an easier to understand and more performant framework.

Long version - Every React component provides a render function that generates the HTML fresh when it is called. These Render functions make the calls that construct the child elements of the component on the fly, and typically contain decision logic that determines which children, if any, are constructed. Best practices in Render is to have a single root component (the Scene in our world) who’s render function creates the next level down, which in turn creates the next level down, and so on generating a very dynamic HTML representation of the DOM. This is a powerful approach. Routing, for example, is handled by an easy-to-read switch/case structure in the root component’s render function.

The HTML that is generated is not sent to the browser for rendering. Instead, the React engine compares compares the most recent HTML with the last generated HTML to identify differences. These differences are sent to the browser as DOM-manipulation calls. I believe the React system runs all the HTML-generating Components in web workers so that only the DOM and DOM manipulation logic runs the main thread. This is what makes React so performant.

The React system provides all the DOM event callbacks as javascript-wrapped as component methods. The developer is responsible for updating the state of the component. I find this both a little tedious and very useful, since it puts into the code what should be happening with the class. State updates trigger re-rendering of the sub-tree, which seems like a lot of overhead but does not result in any appreciable lag. React is either very smart about caching objects or their construction processes are minimal.

So the bottom line isn’t that I want to integrate Infamous with the React framework, though that might be a very smart idea to be discussed in another thread. I’m proposing that we follow the React pattern and keep all state information local in the developer’s code. After two weeks of getting up to speed on React I don’t see any advantage to providing DOM query or manipulation at all.

Hey @TDSFugal, glad to have you here.

I haven’t really familiarized myself with React yet, so I’m afraid I can’t add much there.

Ideally, any parts of the engine that touch DOM should be contained within a plugin/extension. If the goal is to have a particular extension use React’s pattern, then I don’t see any reason why we shouldn’t use React itself–barring legitimate concerns such as performance or size.

I know that having default extensions be dependent on third party libraries is frowned upon, but maybe the best plan might be to have no default extensions “out of the box”.

Anyways, I’ll leave it at that since I’m straying from the topic here. My main point was that Famous’ DOM implementation was far too highly coupled to the engine, and perhaps if we could achieve a more decoupled system, design considerations such as this one would matter far less, since rather than refactoring the entire engine, the damage would be contained to a plugin/extension/etc.

1 Like

@TDSFugal Welcome!

I agree. I love the component code we write in React. Everything for a component can be in one class: the markup, the styles, the logic, and the data interface). I do feel it’s easier to reason about.

Yes! I’m using this in a couple project (React+Famous with my fork of react-famous, but switching to React+Infamous). It’s really nice.

Seems like it would be lots of overhead, but the way it’s implemented it’s totally not, yep. The things we return from a React render method are just simple object literal specs, and the actual Objects (DOM Elements or Infamous scene graph nodes) are only created and destroyed behind the scenes based on the results of the internal diff. There are two diffs that happen: one is between the returned render specs and the object instances represented by those specs (which are instances of the classes we make with React.createClass or by extending React.Component, then a diff between our React component instances and the actual DOM, Native Widgets, Scene Graph, etc (depending on which React renderer we use). The bottom line is when it comes time to change the actual DOM or Infamous scene graph, on the bare minimal changes will be made to those.

React has refs (this and this) which essentially are similar to querying. THe only time I’d really find querying useful is to query the “owned” elements of a React component (those are the “elements” that are returned from a component’s render function). So I think as long as querying doesn’t escape outside the component, it’s fine. Querying for children of an “owned element” would be something we should avoid because then we’re going to start modifying things using logic that lives outside of the component we queried, the component where the logic should actually be.

It definitely would be nice to use these ideas in the design of our engine.

@oldschooljarvis Give React a try. You’ll notice the API is really small and that the pattern it introduces for writing the structure of UI is just a pattern for staying organized and being able to more easily reason about how the structure of a UI will change. It’s not really a rendering engine in the sense of graphics, but a rendering engine in the sense of rendering tree structures based on easy to understand coding practices (it happens to be that the default tree structure that it renders is DOM out of the box, but react-famous is using it for scene graphs, and react-native for native UI trees).

Yep, I think so too. Or default ones that happen to be official ones written by us for the engine and they serve as examples of how to make 3rd party ones (f.e. we can have our own simple CSS3D DOM renderer out-of-the-box, while Three.js could separate). What do you think?

Yep, totally.

Hey @TDSFugal, great seeing you here!

We’ve all gone a bit off topic though; I’m sorry I wasn’t clearer in the beginning. I didn’t mean using jQuery for DOM operations (which I’m against), I meant using a jQuery inspired API for operations on the Scene Graph. So back to:

But that’s already looking like jQuery, which builds upon selectors :slight_smile: I find a lot of the methods super useful:

  • node.children(selector) - matches against immediate children
  • node.find(selector) - any matching descendent
  • node.parent(selector) - any matching ancestor
  • node.closest(selector) - first ancestor that matches

More so, all methods are chainable and can act on all matches nodes, with custom plugins, so we can do things like:

  • scene.find("#box').setPosition(100,100);
  • node.children().forEach(function() { .... });
  • listView.children('.selected').shake();

I think jQuery adoption exploded because it took DOM manipulation and made it so intuitive and easy. Especially navigating around a tree structure which can get quite complicated especially when it’s structure can change. And many developers are already proficient in jQuery so there’s no learning curve.

These are also great because the chaining nature lends to easy asynchronous usage, where functions could be run only when the data has been retrieved from the worker, or even the entire function (if it’s not a closure) passed to the worker to execute there - depending on the API.

And yeah, fine with this being a plugin :>

1 Like

I think React’s component-based encapsulation of the UI hierarchy eliminates the need for excesive tree navigation like we see with jQuery (which can cost CPU). When components are coded properly in React, the most you’ll ever need to use is the ID-like ref property to get the owned child of the hierarchy within your component. I think this is nice because it forces you to encapsulate functionality of your component and not have to worry about some other tree-traversing logic affecting your UI component in unknown ways.

In jQuery, a properly architected UI has two parts: A DOM hierarchy and some logic that gets that hierarchy from the DOM, but it’s really easy to stray from that and access elements all over the place in an entangled mess. In React, the relationship between functionality and a piece of DOM tree is very explicit.

I think I’m on the side of the fence for not using this sort of traversal, barely hanging on with my cat claws. x} Maybe we can limit traversal and selectors to the immediate scene graph of components, and not let the traversal go outside of that scope? I can see that subset be super useful moreso that global selectors (it’s like global-scope vars vs module-scope/class-scope vars).

I haven’t touched react yet but I’m well aware of all it’s advantages. I definitely think we should embrace a react’ish API (for similar reasons, because it covers a lot of needs and because a lot of people will already be familiar with the API). If the react style eliminates the need for constant tree traversal, I’m more than happy do away with it. I’m nowhere near an API for building UI components yet, but when I get there, I’m going to try react-like first, and then see what other requirements there are.

P.S. jQuery is so slow mostly because it touches the DOM. If we do our own tree traversal (with our own indexes, etc) it could be super fast.

2 Likes

My 2c here:

jQuery promotes the use of a global name space, which is an anti-pattern.

This would be marvelous if one was required to specify a starting point in the scene graph from which to begin the search, eg:

node.find("#box").setPosition(100,100)

rather than:

scene.find("#box").setPosition(100,100)

Of course one could circumvent this structural attempt to enforce best practices by specifying the root node of the scene graph to begin their search, but at least they are intentionally doing so.

D3 is a great example of this being done well, but it isn’t the most performant library out there.

ap

1 Like

I have a feeling I don’t like adding this directly to the API. Imagine this: some developer inherits a complex app. The developer needs to make some quick adjustments. Thanks to this API, the developer is able to do so. A second developer inherits the app and needs to make some changes. The second developer now has to make changes to the app while having the first developer’s (possibly hacky) modifications in mind. A third developer might need to consider both previous sets of compounded modifications. As the app gets bigger, changing the app could become overly complicated since modifications may not be modular and encapsulated.

With a modular approach (like React’s, for example) we’ll have a higher probability of well-structured organized code from the get go (it’s still possible to make spaghetti code no matter which patterns we use, which is why I mention probability :laughing:).

1 Like