ReactJS Why is my DOM / `display` only rendering 'display: none'? - javascript

Driving me a bit nuts here...
I want to click a Preview button and it generates a Preview Pane for text input. I thought i'll get the div's inline-style linked to React state.
<div className="preview-container" style={{ display: this.state.showPreview ? 'visible' : 'none' }}>
{
this.state.showPreview &&
<Preview
htmlString={value.toString('html')}
showOrHidePreview={this.togglePreview}
/>
}
</div>
But even though i can see state changing when i click the button,
AND i've successfully changed a border colour by linking it to state - for some reason changing display to none just sticks. it wont move off it.
Any ideas?
Updated (fairly standard toggle handler):-
togglePreview = () => {
if (this.state.showPreview === false) {
this.setState({
showPreview: true,
})
} else {
this.setState({
showPreview: false,
})
}
}
Thanks v.much #AdrianoRepetti for the suggestion of display: block rather than visible. Not sure where i picked up display: visible from.
If anyone else happens across this, i also found a hack using opacity: 10 vs opacity 0. Though that was scraping the barrel a bit.

There are several issues with this code:
1.
display: visible is incorrect and will have no effect (ignored by browser). The correct value here is display: block.
2.
You are removing the contents when showPreview is false, so display: none has no effect either.
3.
When showPreview is false you are asking React to render the value false. This is wrong, and you will get error.
4.
The max value of opacity is 1.
5. (potentially)
What is the initial value of showPreview? If undefined, your toggle will set showPreview to false, giving you the impression that it didn't do anything at all.

Related

Wait for css of element to be updated when it is dynamically changed with JS/Jquery

So after a lot of searching about an issue that I am having, I figured out that the issue that I am having is, because when I apply css to the element, the info is not immediatelly available, here is what I mean:
I have an element that I am hiding and showing with display: block; and display: none;, the issue is that right after I show the element, I need to obtain its height:
$element.css({ display: "block"});
console.log($element.outerHeight()); // <- this returns 0
So I started experimenting with timeouts at some point, and even with a 1ms timeout, it still works and returns the correct height:
setTimeout(() => {
console.log($element.outerHeight()); // with 1ms timeout, it works and gives real height
}, 1)
After figuring that out, with 1 ms delay it could not be some inherited transition or anything, so that got me thinking that maybe, between the jump from display: block; to .outerHeight() something is not yet updated, after some digging I found this:
Quote: "You'll see different behaviors in different browsers since jQuery is updating the markup with inline styles and the browser decides when to repaint."
Does jQuery's css method block until the change is fully applied?
And this seems to be my actual problem, so the question now is, how do I figure out when that repainting has happened, I have tried with onload, load events but none of them get triggered.
I have also tried the suggested code in that post with animation:
$element.animate({
display: 'block'
}, {
duration: 0,
done: function(animation, jumpedToEnd) {
console.log($element.outerHeight());
}
})
But that did not work either.
The issue you are having is that you do not really have control on how the browser is displaying changes. The information is not immediately available, as you have pointed out and even though doing timeouts is technically a solution, it lacks elegance and endangers your UI of getting into callback hell.
What you will need to test in order to see whether more elegant solutions work is
1. Whether it works with 0 milliseconds
If it works with 0 milliseconds, then basically the UI is refreshed just after the function is successfully executed and before the next function from the event loop is running.
2. Whether doing it without jQuery behaves differently
Compare the behavior of
$element.css({ display: "block"});
with
for (let index = 0; index < $element.length; index++) {
$element[index].style.display = "block";
}
is equivalent. If so, then it's not a jQuery problem. If not, then it's a jQuery problem.
Conclusion
As a general guidance, you will probably need a callback-like behavior and to achieve that you will probably want to use the promise architecture. If it works.

Decide order of CSS Transitions

Funky problem i'm having, although i bet theres a slick way to solve it.
The circumstance is that i have a table that has three column sort states: completely unsorted, in which case i dont want any icon present, sorted ascending, in which i want an up arrow, and sorted descending, in which i want a down arrow.
clicking the column header should take you through these three states.
starts off => click => ascending => click => descending => click => back to off
This all works fine and dandy, except for the fact that i want to use the same Caret element, and then use css transition's to fade it in from opacity: 0 to opacity: 1 , then on click rotate it 180deg to show a down arrow for descending, then finally when clicked again to remove the sort, i want it to fade out WITHOUT ROTATING BACK TO 0 DEG
That last part is where the problem lies.
EDIT
I have only recreated the behavior in the sandbox, but i am really using react-table, so there are only three possible states since it is controlled by the package:
initial state: {showCaret: false, flipped: false}
first click: {showCaret: true, flipped: false}
second click: {showCaret: true, flipped: true}
third click, back to initial: {showCaret: false, flipped: false}
the state changes are controlled by react-table, so i cant setTimeout on the flipped variable.
I am looking for a purely CSS way to achieve this goal without manipulating the way the state changes, if possible
END EDIT
I've attached a codesandbox to demonstrate. First click Show Caret, then Flip Caret, then Hide Caret. The css is set up basically the same as mine is currently in my actual project too.
https://codesandbox.io/embed/admiring-rain-svsc9?fontsize=14&hidenavigation=1&theme=dark
It sounds like what you want is for the arrow to disappear, but not to rotate back to its starting orientation as it disappears.
Since you are handling and tracking all this with React state, you could just set those two states separately, timed .3s apart (since that is your CSS transition time).
You could do this in a number of ways. To demonstrate it, in this fork of your example I have you just setting the on/off visibility to off, and then separately, in componentDidUpdate, I have it watching for whenever it's turned off, at which point it waits 300ms (.3s) and then sets the rotate state back.
componentDidUpdate(oldProps, oldState) {
if (oldState.showCaret && !this.state.showCaret) {
//it was just hidden
setTimeout(() => {
this.setState({
flipped: false
});
}, 300);
}
}
https://codesandbox.io/s/sparkling-pine-9igec
EDIT, with CSS only solution
https://codesandbox.io/s/pensive-wilbur-opxzf
/* flipped taking care of rotating the img tag */
.image {
transition: transform 0.3s linear 2s;
}
.flipped {
transform: rotate(180deg);
transition: transform 0.3s;
}
Change
onClick={() => this.setState({ showCaret: false, flipped: false })}
To
onClick={() => this.setState({ showCaret: false })}
and it should work.

React Spring: dynamic values in useTransition() when using it alongside react-router

In this example you can see some nice transition between pages triggered by the route change. (took from LevelUp Tutorials' React Animation course, thanks a lot Scott Tolinski).
Now I'd like to make these transitions happen in both directions, depending on which page it's transitioning to (and from), for instance:
Page One -> Page Two (both pages transition from left to right)
Page Three -> Page One (both pages transition from right to left)
etc
From that first example I created this example where the value of x is dynamic and should be evaluated to either 100 or -100, based on the direction of the transition.
I haven't fundamentally understood how useTransition() works, and the documentation is rather limited. The examples look amazing but are quite hard to understand.
This example seems to do a similar thing to what I'm trying to achieve but the code feels like black magic: the y property of each object returned from rows.map() appears to be related to the y value on the functions assigned to enter and update properties because if I remove that I get the error: Cannot read property 'replace' of undefined. How does that work?
This problem has two part.
determining the direction
change the animation
I created an example for solving the second part. When the user click page one I reverse the animation.
const reverse = location.pathname === '/';
const transitions = useTransition(location, location => location.key, {
from: { opacity: 0, transform: `translate3d(${reverse ? '-100%' : '100%'},0,0)` },
enter: { opacity: 1, transform: "translate3d(0,0,0)" },
leave: { opacity: 0, transform: `translate3d(${reverse ? '100%' : '-100%'},0,0)` },
// "initial: null" should only disable the 'from' initial transition, not the subsequent 'leave' transition (on the first manually triggered transition)
initial: null
});
Here is the sandbox: https://codesandbox.io/s/spring-transition-with-routes-215t8
For the first part to determine when to reverse the animation, I would store the path at each click and compare the next one to the previous. There is an example of storing the path here: Check history previous location before goBack() react router v4
I hope it helps.

Disable IE11 resize controls inside contenteditable divs [duplicate]

E.g. I have the following layout:
<div contenteditable="true">
<span class="text-block" contenteditable="false">
<span contenteditable="false">Name</span>
<a href="javascript:void(0)">
<i class="small-icon-remove"></i>
</a>
</span>
​</div>
So, how to disable this:
and this:
I spent on this a lot of time myself, when trying to completely hide control selections (this is how they are called) in CKEditor's widgets. Unfortunately I don't have a good news.
Solution 1
First of all, there's a mscontrolselect event. When I found it (and the fact that its name has an ms prefix) I was very happy, because according to MS it should be preventable.
But it turned out that it's totally unstable. Sometimes it is fired, sometimes it isn't. It varies between IEs versions, DOM structure, attributes, which element you click, is it a block element, etc. The usual MS's crap. But you can try:
function controlselectHandler(evt) {
evt.preventDefault();
}
document.body.addEventListener('mscontrolselect', controlselectHandler);
However, this will completely block selection (if it worked). So you'll make those elements unselectable at all.
Solution 2
Then there's a second option, more reliable - moving selection somewhere else after such element was clicked. There are few ways this can be implemented. In CKEditor we're fixing selection on mousedown... and mouseup because (again) sometimes it's not enough for IE and it depends on dozen of conditions. You could also listen to selectionchange event and fix selection there.
However, again, we're also talking about blocking selection of such element.
Solution 3
Therefore, the third option is to block not selection, but the resizestart event. CKEditor combines this with enableObjectResizing command: https://github.com/ckeditor/ckeditor-dev/blob/a81e759/plugins/wysiwygarea/plugin.js#L211-L218. This solution will prevent resizing, but of course will not hide those ugly borders.
Solution 4
As I mentioned, I worked on this problem in CKEditor. We managed to make it possible to have non-editable elements inside editable, but with completely controllable and unified behaviour between browsers. The complete solution is too complex to be explained on StackOverflow and it took us months to implement it. We called this feature widgets. See some demos here. As you can see there are no control selection when non-editable element is selected. The selection appears on a short moment only between mousedown and mouseup, but only in specific cases. Except for that everything works as it would be native (although it's a completely fake thing).
Read more in the Introduction to Widgets and in the Widgets Tutorial.
This post was critical when solving this issue for me (works in tinyMCE):
How to Remove Resize handles and border of div with contentEditable and size style
By placing a contenteditable DIV within a non contenteditable DIV the handles do not appear in IE or FF but you can still edit the content
Ex.
<div class="outerContainer" contenteditable="false">
<div class="innerContainer" contenteditable="true">
</div>
</div>
Solution 5
When the focus is moved to child control change the content editable element attribute value to false and same way once your focus leaves from child control again set the content editable to true.
To disable the resize handles, all I had to do was add the following for IE11:
div {
pointer-events: none;
}
For firefox executing this line after the contenteditable element has been inserted works:
document.execCommand("enableObjectResizing", false, false);
What solved the problem for me was removing a max-width: 100% !important; line from the CSS properties of the DOM elements within the contenteditable DIV. Hope it helps!
BTW this does not happen on MS Edge... fingers crossed that this shows a movement in the right direction by MS :)
I had the same problem. It appears that from previous posts here there are certain behaviors that IE recognizes and will add this paragraph focus/resize. For me it was because I had a style for paragraphs within the contenteditible div.
Removing:
div[contenteditble="true"] p{
min-height:1em;
}
Fixed it for me.
SOLVED!
On placing the non content-editable span within a content-editable BODY, it started showing a resize-able SPAN container. What just fix my problem was a simple one-liner CSS style
pointer-events: none; on the inner SPAN tag.
min-width: 1.5cm;
display: inline-block;
pointer-events: none;
<body content-editable="true">
<span>Sample Text</span>
</body>
overflow:hidden also can cause this issue, like:
ul, ol {
overflow: hidden;
}
I have the same problem with CKEditor 4.4.7 in IE11. As a workaround, I save the current dimensions of an element on "mousedown" and set the "min-width", "max-width", "min-height" and "max-height" style properties to it's current dimensions. By that the element will be displayed in it's original size during resize. On "mouseup" I restore the style properties of the modified element. Here is my code:
$('textarea').ckeditor().on('instanceReady.ckeditor', function(event, editor) {
var $doc = $(editor.document.$);
$doc.on("mousedown", "table,img", function() {
var $this = $(this);
var widthAttrValue = $this.attr("width");
if (widthAttrValue) {
$this.data("widthAttrValue", widthAttrValue);
}
var widthStyleValue = this.style.width;
if (widthStyleValue) {
$this.data("widthStyleValue", widthStyleValue);
}
var width = widthStyleValue || widthAttrValue || String($this.width())+"px";
var height = this.style.height || $this.attr("height") || String($this.height())+"px";
$this.css({
"min-width": width,
"max-width": width,
"min-height": height,
"max-height": height,
});
$doc.data("mouseDownElem",$this);
}).on("mouseup", function() {
var $elem = $doc.data("mouseDownElem");
if ($elem) {
$elem.removeAttr("height").css("height","");
var widthAttrValue = $elem.data("widthAttrValue");
if (widthAttrValue) {
$elem.attr("width", widthAttrValue);
$elem.removeData("widthAttrValue");
} else {
$elem.removeAttr("width");
}
var widthStyleValue = $elem.data("widthStyleValue");
if (widthStyleValue) {
$elem.removeData("widthStyleValue");
}
$elem.css({
"min-width":"",
"max-width":"",
"min-height":"",
"max-height":"",
"width": widthStyleValue || ""
});
if (!$.trim($elem.attr("style"))) {
$elem.removeAttr("style");
}
$doc.removeData("mouseDownElem");
}
});
});
Here's what I did to fix this problem. For me this would only happen when the contenteditable element was empty and the resize handles would disappear when there was content so I created the following CSS only solution to go about this:
[contenteditable]:empty:after {
content: " ";
}
The idea behind the solution is whenever the contenteditable field is empty it applies a blank space pseudo element thus removing the resize tags from showing up when the user selects the contenteditable field. Once the user has entered anything then the pseudo element disappears.
Note, because of the use of pseudo elements, this fix only works on IE9 and up.
I had the same problem because I put CSS rules for the max-width onto all child elements within the contenteditable. Removing it or restricting it to images did the trick.
[contenteditable] * { max-width: 100%; } // causes the issue
[contenteditable] img { max-width: 100%; } // works fine for me
Make sure that no <p> elements are affected by the max-width property.
Nothing anyone else recommended here or in other threads really worked for me, but I solved it by doing:
[contenteditable="true"] p:empty {
display: inline-block;
}
This way the resize boxes disappeared, but I could still set my cursor below or in the P blocks to edit them.

Not sure what affecting button display - updated

I have and radio button selection and given each radio button a progress bar, progress bar are display:none but when selected a radio progress bar will show(), same to the button.
Now my problem is sometime when I selected a radio button, the button show like this:
But sometime when I selected a radio button, the button has been push to the bottom and cant even seen:
I not sure where this problem come from, but I guest maybe the progress bar blocked it?
Maybe I have miss something in my code that I don't know. Anyone able to help me check for this problem ?
Click here for jsfiddle
Updated part
I found out how to make the button push to bottom , hover in and hover out the div 4 to 5 times then select one option, then the button will push to bottom. So far haven't found any solution, anyone know how to fixed please help. Thanks a lot.
Remove the margin-top: -22px from .popup_survey_whitebox_percent and replace with
position: relative;
top: -22px;
Fiddle
Finally I found a way to fixed this problem .
$("#popup_survey_whitebox").hover(function () {
$('#popup_survey_whitebox_content').finish().animate({
opacity: 1,
height: "toggle"
}, 500, function () {
$("label#popup_survey_label_title").text(orig); // Here put the original text.
}).css('position', 'relative');
}, function () {
$('#popup_survey_whitebox_content').finish().animate({
opacity: 1,
height: "toggle"
}, 500, function () {
$("label#popup_survey_label_title").text(newText); // Here put the new text with "..."
}).css('position', 'relative');
});
change $('#popup_survey_whitebox_content').stop() to $('#popup_survey_whitebox_content').finish() for both animate
As you mentioned in your answer, finish works, but introduces some quirkiness for the experience if you hover in/out quickly (it jumps to the end of the animtation immediately, and can "flicker").
This issue can be addressed by reseting the height attribute on the hover 'out' logic. When you hover in/out quickly, the height value is set, which is then used after.
Here's a fiddle with the fix: http://jsfiddle.net/v07vt9gz/5/
And the logic:
$(this).css('height', ''); // reset value

Categories

Resources