How to use CanJS with VelocityJS? - javascript

I'm using CanJS (with StealJS) to build a quizz app, and I have quizz-question component that is rendered for each question!
I wonder how to make a transition with velocityjs each time quizz-question component is removed for answerd question and inserted for the new question?
Any help is appreciated!

A key element with using transitions on changing data is that the transition has to complete before removing the element from the DOM (which would remove the element from display immediately).
As far as I know, CanJS doesn't have a mechanism to wait for a process before removal of nodes, so the appropriate workaround is to have a node that isn't removed when content changes. You can structure your markup inside of that node, but the Velocity transition has to happen on the permanent node to make the transition successful.
For the example of fading in and fading out, setting the content to put inside the transition container should use an async setter (with val and resolve arguments) to make the transition work correctly. First fade out (and use the returned promise to wait), then update the markup with the new content (using resolve()), then fade in. I made a JSBin that demonstrates this concept, though the content inside the transition is very simple in the demo.
https://jsbin.com/lesagebomu/edit?html,js,output
In the case of a quizz-question component, you'd want to render the whole component inside of the div in fade-in. There's probably also a way to generalize this into a higher-order component using e.g. <div class="fade-in"><content /></div> and some data manipulation.

Another way to do this would be to dispatch events on the elements being added and removed and then listening to these events in the view:
<li on:click="../removeQuestion(question)"
on:removing:by:question="fadeOut()"
on:inserting:by:question="fadeIn()">{{question.name}}</li>
You can see this here: https://jsbin.com/lepuxaq/edit?html,js,output

Related

MutationObserver and DOM timings

I am using said MutationObserver to watch for changes on an HTMLElement.
I dynamically add children to a div and the observer does a nice job at executing the provided callback anytime changes are detected in the div's tree.
I am adding the children all together (by mounting a React component) and there is an action that I need to perform only when I know that all of them are added; if I perform that action also before all the children are added to the DOM, then I would irreparably mess everything up.
The observer possibly would run the callback a bunch of times within a fraction of a second just because I added a bunch of children, so a possible solution would be to set a timeout for this particular action.
Now since the action is vital for the interactivity of the page I need to wait as little as possible.
My question is: is there an order of magnitude for the time needed to add a handful of children to an element? Something like 1ms, 100ms or 1 second? How long is it safe to wait?
But also: are you aware of any other possible solution to the problem?
PS: my question is a generalisation of this question, which has been solved by checking for the appearance of a particular entity (a background-image), but I am working with a reusable custom hook and waiting for the appearance of an element that could be an input as it could be a button and by the way it would not be the only one of its type among the children being added to the div, nor it is granted that it will be present on the DOM once all the children are added. I just need to check for its presence once all the children are added and that's it. A different approach is needed.

jQuery to detect DOM changes

I am working on a jQuery plugin where I need to manipulate DOM for some insertions and deletions and I am required to work with some specific class (say class demo). I have to make it work with Angular/AngularJS/JS or any framework.
Problem I am facing is How can I detect that particular class visibility in DOM If user is hiding that element or say loading another component or routing to another page. (like in Angular2/4/5 we normally do with ngIf etc. )
I came across this livequery but it does not work as expected. Any link/example/suggestion would be grateful.
PS: and also I don't want to provide any function from my code, which is required to call in their component etc

Using JQuery plugins that transform the DOM in React Components?

Some JQuery plugins don't just add behavior to DOM nodes, but change them. For example, Bootstrap Switch turns
<input type="checkbox" name="my-checkbox" checked>
into something like
<div class="bootstrap-switch bootstrap-switch-wrapper bootstrap-switch-on bootstrap-switch-large bootstrap-switch-animate">
<div class="bootstrap-switch-container">
<span class="bootstrap-switch-handle-on bootstrap-switch-primary">ON</span>
<label class="bootstrap-switch-label"> </label>
<span class="bootstrap-switch-handle-off bootstrap-switch-default">OFF</span>
<input type="checkbox" name="download-version" checked="" data-size="large" data-on-text="3" data-off-text="2.0.1">
</div>
</div>
with
$("[name='my-checkbox']").bootstrapSwitch();
Which doesn't jive with React:
Uncaught Error: Invariant Violation: findComponentRoot(..., .0): Unable to find
element. This probably means the DOM was unexpectedly mutated (e.g., by the
browser), usually due to forgetting a <tbody> when using tables or nesting <p> or
<a> tags. ...<omitted>...`.
Is there a recommended technique for incorporating these plugins into React components? Or do they fundamentally break the assumptions of React and cannot work with it?
No, react will react (haha) badly to anything that modifies its own component dom structure outside of react. This is something you don't ever want to do. The recommended solution would be to replicate the functionality of whatever you're trying to do with a jquery or similar plugin, in react.
Having said that, there is a reasonable way to do this for specific instances where you just can't do without it, but it essentially means wrapping some non-react dom inside react.
Example:
var Example = React.createClass({
componentDidMount: function() {
var $checkboxContainer = $(this.refs.checkboxContainer.getDOMNode());
var $checkbox = $('<input />').prop('type', 'checkbox');
$checkboxContainer.append($checkbox);
$checkbox.bootstrapSwitch({});
},
render: function() {
return (
<div>
<div ref="checkboxContainer"></div>
</div>
)
}
});
Now of course you are rendering a component with a nested div. The nested when mounted to the dom for the first time that nested div will get a checkbox appended to it by jquery, which will then also execute our jquery plugin on it.
This particular example component has little point to it, however you can see how this might integrate into a more complex component while still allowing you to re-render and react to state changes etc. You just lose the ability to directly react to events/modify things inside of the checkbox in question which as far as react is concerned, doesn't exist.
Now with the above example if you were to have some react logic to add/remove the nested div, you'd have to have the same logic around that div being inserted be responsible for re-inserting the checkbox and re-initializing it with the jquery plugin. However because react only modifies the dom when needed, this inserted dom content wont be removed unless you do something that modifies the container div in a way that causes it to be removed/re-rendered to the dom. This means you can still access all of the events within react for that container div etc.
You could also make use of the componentDidMount function in react to bind events or callbacks to specific interactions on the checkbox itself. Just make sure to unbind them correctly in componentWillUnmount or wherever it makes sense to do so in the component lifecycle in your specific case.
In this great ryanflorence's tutorial you'll get an idea on how to do this:
Wrapping DOM Libs
Methodology
DOM libs usually manipulate the DOM
React tries to re-render and finds
a different DOM than it had last time and freaks out
We hide the DOM
manipulation from React by breaking the rendering tree and then
reconnecting around the DOM the lib manipulates.
Consumers of our
component can stay in React-land.
Sure, there is such a technique. We're doing these things all the time.
You create React component to wrap jQuery plugin.
Inside of your render(), you return an empty <div ref="placeholder" />
In your componentDidMount method, you retrieve this element by its ref, and initialize your jQuery plugin there.
In your componentWillUnmount, you clean it up. Calling 'destroy', or anything else required to avoid memory leaks.
That's it. Fortunately, it's completely safe to modify DOM in this way in React.
If you want this plugin to react on props changes, things get a bit more tricky. You need to override other lifecycle methods, like componentWillReceiveProps, check whenever props actually changed, and call corresponding plugin methods. I can explain in more details, if you will have specific questions, overall topic is too broad for the comment.
This is more of a philosophical question
React was created to optimize DOM manipulations and has a lot of wiring behind the scenes to do so when a component's state changes via setState
Doing so will cause said wiring to traverse its virtual DOM to find the nodes that need to be updated
If you must use React, whether to try to keep a level of consistency in your coding, your best bet is to apply the JQuery DOM manipulation inside the componentDidMount like so...
componentDidMount(){
this.node = $("#"+this.props.id); // Keep a reference to the node
this.chart = this.node.easyPieChart(); // Apply JQuery transformation and keep a reference
this.percentTitle = this.chart.find(".percent"); // Keep a reference to the title
}
Having done so, on whatever your "refresh" method is, do NOT make any calls to setState, instead, call whatever update method your JQuery component may have, like so...
componentWillMount(){
this.interval = setInterval(this._doRefresh.bind(this), 1500);
}
_doRefresh( percentage ){
// Note how setState is NOT being called here
percentage = percentage || Math.floor (Math.random() * 100) // simulate since we're not getting it yet
this.chart.data('easyPieChart').update(percentage); // call easyPieChart's update
this.percentTitle.text(percentage);
}
At this point, if you're asking why use React at all, well, in my case, this component is an item in a list of other React components and was used to maintain consistency throughout the application... You may have a similar dilemma
If, unlike me, you are unlucky enough that your component doesn't have an update method, and you can't make one, it might be time to rethink the approach altogether

is it possible to view one html element twice on the same page, or must I create a duplicate?

I am creating a site that allows viewing and editing the contents of the 'src-div' contents within the 'edit-div.' I am not editing the src-div directly, because its thumbnailed using css zoom property.
I have considered using knockout.js to bind both elements to an observable. Currently, I have implemented the feature with jquery .html() function: simply set edit-div innerhtml to src-div innerhtml on 'select', and reverse the process after changes are made to edit-div to update the src-div.
I am wondering if I really need 2 divs here, or if there is some way to actually view the same element twice on a page, and any changes made will automatically reflect in both 'views,' elimiating the need to copy innerhtml property back and forth between two elements.
essentially, this is like a mirror effect, without the flip.
the closest thing I found so far is:
http://developer.apple.com/library/safari/#documentation/InternetWeb/Conceptual/SafariVisualEffectsProgGuide/Reflections/Reflections.html
Any recommended practices for performing this task are appreciated.
(Almost) everything you see on a page has a counterpart in the DOM. Everything in the DOM gets exactly rendered one time (apart from pseudo-classes). And every node in the DOM can only have one parent (no exclusions).
Unfortunately you'll have to clone the specific node and add changes to both, as there is no copy & translate mechanism in the current CSS documentation.
If you're using jquery you can use one div and "clone" it. You can read this for more information.
http://api.jquery.com/clone/
If you set the class of the div to the same thing, you can have changes propagated to both. Then you can apply .addClass to the second div to apply a "reflected" affect (if that's your final goal).

Using jquery to add/remove class but elements are not redrawn to reflect

I have two elements (think of two buttons side by side). I dynamically toggle the class "focusd" to change the highlighted effect. However, there's a quirk it doesn't always get redrawn and/or inserted in the DOM. For example, if in chrome I do console.log, I see the class changes (I'm using removeClass/addClass in jquery). But if I go to the Elements tab in the inspector, it shows the classes from before (and in fact, I'm not seeing the redrawing reflecting the toggling of the classes.)
I tried setting the parent div to display none then back to block but that didn't work. It's a "one off" modale screen, so efficiency doesn't matter so I've resorted to this hack where I essentially copy the parent's innerhtml, remove and reinsert the element. Horrible!
// Not sure why I need this hack. But if I don't, the buttons don't seem to get redrawn
var htm = jQuery(".rdata_container").html(); // copy the innerhtml
jQuery(".rdata_container").empty(); // empty and then append back
jQuery(".rdata_container").append(htm);
This seems like a specific quirk that someone must have ran into (I hope). If so, I'd love to know why my changes aren't reflected.
EDIT
Code posted here:
http://jsfiddle.net/roblevintennis/JCZnf/
you can use setTimeout when you are doing the other operation on the element, so for example:
$elm.addClass('hide')
setTimeout(function(){
$elm.removeCalss('hide')
},0);
Or you could force a repaint like so:
$elm.addClass('hide')
$elm.scrollTop; // forces a repaint (might be expensive for large amount of items)
$elm.removeCalss('hide');
These tricks will force the browser to re-draw the change, because there are two things happening here and the browser logic just combines them into one, which isn't the desired behavior.
Not directly an answer to your question, but you can use jQuery's toggleClass function to simplify your code.
Here's an updated version that uses toggleClass() and jQuery 1.6 and AFAICT works fine.
http://jsfiddle.net/JCZnf/7/

Categories