ReactJS + Button with updating text overflowing/exceeding button size - javascript

I've currently got a button module, which, based on an array it is given, generates a button with an Image, and Text. However, now that i started adding translations to the buttons, I noticed that some translations for more complex words, are overflowing out of the button, at an inconsistent way.
The bottom left is the standard/default text in English. There is some inconsistency that I've yet to sort out, but it's all style from my understand. I am still learning how styling buttons works.
All the other buttons show what happens when a different language is presented/selected/used. The buttons update the text when language is updated, so they are dynamically refreshed with new text.
Each button is generated like this:
return <button
key={buttonName}
disabled={disabledStatus}
style={this.props.buttonStyle}
onClick={this.handleButtonClick.bind(this, buttonLowerCase)
>
<img src={...} alt={buttonName}
/>
<span style={spacer}/> || This is just {width: '5px'} for seperation||
{buttonText}
</button>;
buttonStyle is passed down with following parameters from the module:
{
width: '100px',
margin: '5px',
height: '38px',
display: 'flex',
alignItems: "center",
justifyContent: "left",
};
what are possible solutions to this? Keeping in mind, that the buttons cannot grow to be a bigger size than they are currently.
I also do notice that spacer does not seem to be respected, if text is larger than given space, now that I look at it, but have no better solution for forming a bit of space from the text at this point.

If you absolutely need to force the content into the space of the button without increasing the button size dynamically to match the text length, here are some options:
Break the words. See: "word-break" css property.
Make the text smaller where needed.

Related

Why does displaying text inside the document via transform: translateY(-100%) create extra white-space at bottom of page?

Put very briefly
I wrote a vuejs component that includes a small icon that displays some text when being hovered over.
I created a minimal example here: https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136
The special thing is: when the icon is at the very bottom of the page the text is displayed above the icon instead of below such that the page does not have to be extended.
However, even when the text is displayed above the icon, the page is extended without any content (even the html ends before the whitespace).
I tested this in Firefox and Chromium.
I would like to understand where this white-space comes from and how to get rid of it.
I will explain in more detail what I'm trying to do below.
If my entire approach to this does not make much sense I'm also happy for suggestions.
My approach so far
First step: display text when hovering an image
What I want to achieve seems quite straight forward with vuejs at first glance.
I want to have a vue component that shows an image (for example a small info icon).
When one hovers over the image with the mouse a text is displayed that overlays the other content on the page.
This can be achieved in different ways - for example:
Create a component that has a hovered variable like so
<script>
export default {
name: 'ShowBox',
props: ['showText'],
data() {
return {
hovered: false,
}
},
}
</script>
In the template, bind the class of the to be shown text to the hovered variable and handle an #mouseenter and #mouseleave event in the image accordingly like so
<template>
<div class="show-box">
<div class="show-icon">
<img width="50px" #mouseenter="hovered = true" #mouseleave="hovered = false" src="...">
</div>
<div class="show-text" v-bind:class="{'hide': !hovered}">
{{ showText }}
</div>
</div>
</template>
Finally, in the style the class show-box should be positioned and the class show-text should include a position: absolute such that it overlays the text.
The class hide should somehow hide the text - for example with opacity: 0.
So far so good - for sure one can add a lot of stuff to make this more decent but for the sake of the example let's keep it simple.
The result might look as
this (link to picture).
Second step: display image above the icon when at the bottom of the page
Let's imagine the icon is at the very bottom of the page.
If one then hovers the icon the page might have to be extended in order to display the full text.
I want to avoid this and have the text displayed above the icon instead.
For this I took the following steps:
I wrapped the text in an extra div.
The text is now not rendered anymore when the page loads but only when one hovers over the image though a display: none in the style.
<template>
<div class="show-box">
<div class="show-icon">
<img width="50px" #mouseenter="hovered = true, , textRendered = true" #mouseleave="hovered = false" src="...">
</div>
<div class="show-text-wrap">
<div class="show-text" v-bind:class="{'not-rendered': !textRendered, 'hide': !hovered, 'render-top': renderTop}">
{{ showText }}
</div>
</div>
</div>
</template>
Furthermore, I included a life-cycle hook mounted such that after the page is rendered it is checked, whether there is enough space below the show-text-wrap div to display the text.
If not, a variable renderTop = true is set.
The style of the show-text div is now also bound to the renderTop variable such that if it is set (in the case of insufficient space at the botton of the page) the image is rendered above the icon (also see template above).
The mounted hook looks like
mounted() {
this.$nextTick(function () { //to be sure that page is fully rendered
var el = this.$el.lastElementChild; //show-text-wrap element
var rect = el.getBoundingClientRect();
if (rect.bottom + window.pageYOffset + 300 > (document.body.clientHeight)) {
this.renderTop = true;
}
}
)
},
Finally, the render-top class simply translates the text to the top.
.render-top{
transform: translateY(-100%);
}
The result when hovering over an icon at the bottom of the page might look like
this (link to picture).
The problem
This works fine in principle.
The only problem is: when the text is moved to the top as intended, some extra unwanted white-space appears at the bottom of the page (tested in Firefox and Chromium).
The white-space interestingly extends the html document.
I don't understand where this comes from and want to get rid of this since the whole point of moving the displayed text to the top was to avoid the page being extended.
In
this (link to image)
one can see the extra white-space that extends beyond the html document.
I also tried to move the text above the icon by different means for example
.render-top{
bottom: 0;
}
or even with hard coding pixel numbers like
.render-top{
bottom: 100px;
}
but none of these solve the problem.
I created a working minimal example for this https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136
where the problem can also be seen.
Any help is greatly appreciated - many thanks in advance!
A relative positioned element behaves as if it hadn't been moved
By now I found out the answer myself.
I will post it in case someone finds this useful.
The goal was to always move the text-box inside the viewport.
However, in the style the show-box's position was defined as position: relative; and only its direct parent show-text-wrap is absolutely positioned.
According to https://developer.mozilla.org/en-US/docs/Web/CSS/position a relative positioned element is offset relative to its original position.
This will not change the position of any other elements in the document.
This means that the layout of the document will behave as if the element was still at its original position.
Since the <show-box> originally reached beyond the <html> element such that the page was extended this is still the case regardless of the fact that the element appears moved into the document.
So in order to fix the problem change
.show-text {
position: relative;
}
to
.show-text {
position: absolute;
}

Design breaking while using flex with dimension in react native

Using import {dimension} from 'react-native' and flex:1 at the same time in css style and for some devices design got broken when there is input field present inside the js. CSS is so simple that's it should not be broken
MainContainer: {
height : Dimensions.get('window').height,
width : Dimensions.get('window').width,
backgroundColor: '#fff',
flex: 1
}
and moreover there is slight 1 px blank space for some android devices.
Earlier when I have started coding with react-native I have faced the same issue while using the same css style.
You should read the documentation first carefully to get idea about flex.
flex will define how your items are going to “fight” over the
available space along your primary axis. Most of the time you will
want your app container to be flex:1 to take all of the screen
height. Space will be divided according to each element flex property.
In the following example the red, yellow and the green views are all
children in the container view that got flex:1. The red view got
flex:1 , the yellow view got flex:2 and the green view got
flex:3 . 1+2+3=6 which means that red view will get 1/6 of the
space, the yellow 2/6 of the space and the red 3/6 of the space. I
think you got it…
To get more clear idea about the above lines please refer to this medium.com's post
And basically we don't use dimension while developing app using react-native .
MainContainer: {
height: '100%',
width: '100%',
backgroundColor: '#fff',
}
This will be enough to design the main container. Also If you are using Input field then I will suggest to use scrollView
I think My answer will help you.

Display dynamic html content like an epub/ebook, without converting html to epub format?

I want to create a responsive, mobile optimized reading experience similar to an epub/ebook reader, like the Kindle app, or iBooks, using dynamic html as the source.
Imagine a long article or blog post that requires a lot of vertical scrolling to read, especially on a small mobile device. What I would like to do is break the long page into multiple full-screen sections, allowing the user to use left/right navigation arrows and/or the swipe gesture to "page" through the article.
There are many JS libraries available that can create a "slide show" or "carrousel" of pre-defined slides (using divs or other container elements). But I want the text and html content to dynamically re-flow to fit any device viewport and still be readable... just like an epub/ebook user interface, like the Kindle app or iBooks. So, for the same article, there would be many more "pages" on a phone than there would be on a tablet or desktop viewport, and those "pages" would need to be dynamically created/adjusted if/when the viewport size changes (like switching from portrait to landscape on a mobile device).
Here is an example of a javascript .epub reader: epub.js
... notice the responsive behavior. When you resize your viewport, all the text re-flows to fit the available space, increasing or decreasing the total number of "pages". The problem is that epub.js requires an .epub file as its source.
What I want is the same user interface and functionality for an html page.
I have searched and searched for some kind of library that can do this out of the box, but haven't been able to find anything.
I realize that I could use a conversion script to convert my html page into an .epub file, and then use epub.js to render that file within the browser, but that seems very round-about and clunky. It would be so much better to mimic or simulate the .epub reader user experience with html as the direct source, rendering/mimicking a client side responsive ebook user experience.
Does anyone know if something like this already exists, or how I could go about building it myself?
The crucial functionality is the dynamic/responsive text-reflow. When the viewport dimensions are reduced, the text/content needs to reflow to the next "page" to avoid any need for vertical scrolling. I don't know how to do this efficiently. If I were to code it myself, I might use something like the jQuery Columnize plugin, setting all columns to width: 100vw; height: 100vh, so that each column is like a "page", and then figuring out how to create a swipe UI between those "pages".
Any help is much appreciated!
This becomes very difficult if the html page is complex, eg with precisely positioned elements or images. However if (as in the epub.js example) the content consists only of headings and paragraphs, it is achievable.
The basic idea is to progressively add content until just before the page overflows. By keeping track of where we start and stop adding content, clicking to the next page is a case of changing the page start to the previous page end (or vice versa if you're going back).
Process for reshaping content into pages
Let's assume you have all your content in one long string. Begin by splitting all the content into an array of words and tags. It's not as easy as splitting by whitespace as whitespace between < and > should be ignored (you want to keep classnames etc within each tag). Also tags should be separated as well, even if there is no whitespace between the tag and a word.
Next you need a function that checks if an element's contents overflow the element. This question has a copy-paste solution.
You need two variables, pageStart and pageEnd, to keep track of what indexes in the array are the beginning and end of the current page.
Beginning at the index in pageStart you add elements from the array as content to the page, checking after each add whether or not the contents overflow. When they do overflow you take the index you're up to, minus 1, as the index for pageEnd.
Keeping tags across page breaks
Now if all's ticketyboo then this should fill the page pretty well. When you want to go to the next page set your new pageStart as pageEnd + 1 and repeat the process. However there are some issues that you may want to fix.
Firstly, what happens if the page overflows in the middle of a paragraph? Strictly speaking the closing tag, </p>, is not required in HTML, so we don't need to worry about it. But what about the start of the next page? It will be missing an opening tag and that is a major problem. So we have make sure we check if the page's content begins with a tag, and if it doesn't then we get the closest opening tag prior to the current pageStart (just step back along the array from pageStart) and add it in before the rest of the content.
Secondly, as shown in the example, if a paragraph continues onto the next page, the last line of the current page is still justified. You need to check if pageEnd is in the middle of a paragraph and if so add syle="text-align-last:justify;" to the opening tag of that paragraph.
Example implementation
A pen showing all this in action is at https://codepen.io/anon/pen/ZMJMZZ
The HTML page contains all content in one long element. The content is taken directly from the container #page and reformed into pages, depending on the size of #page. I have't implemented justifying the last line if a page break occurs within a paragraph. Resize the #page element in the css and see how the content resizes itself - note that since the page size is fixed you'll have to use click forward and back to trigger a recalculation. Once you bind the page size to the window size, recalculating pages on the fly simply involves adding a resize event listener to the window that calls fillPage.
No doubt there are numerous bugs, indeed it will sometimes display things incorrectly (eg skipping or repeating words at the beginning or end of a page), but this should give you an idea of where to start.
Take a look at this repository on GitHub. Otherwise, you can create a one-page website with many sections, each one as high as the viewport, by using only CSS (demo):
.section { height: 100vh; }
or by using JavaScript, adding an anchor to each section to navigate between them, and applying a responsive unit (my demo) for the text of each section, to adapt it on resize... Something like this:
var curr_el_index = 0;
var els_length = $(".container").length;
$(".next_section").on("click", function(e) {
curr_el_index++;
if (curr_el_index >= els_length) {
curr_el_index = 0;
}
$("html, body").animate({
scrollTop: $(".container").eq(curr_el_index).offset().top
}, 300);
return false;
});
$(".previous_section").on("click", function(e) {
curr_el_index--;
if (curr_el_index < 0) {
curr_el_index = els_length - 1;
}
$("html, body").animate({
scrollTop: $(".container").eq(curr_el_index).offset().top
}, 300);
return false;
});
* {
border: 0;
margin: 0;
padding: 0;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
body {
background-color: #1a1a1a;
}
section {
height: 100vh;
background-color: #eee;
border: 2px solid red;
font-size: 6vw;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="container">Section 1 Previous Next</section>
<section class="container">Section 2 Previous Next</section>
<section class="container">Section 3 Previous Next</section>
<section class="container">Section 4 Previous Next</section>
<section class="container">Section 5 Previous Next</section>
EDIT #1
An idea of algorithm, that come from a my codepen, that uses the same jQuery plugin:
Create your reader layout, copying the whole text in it
Use this jQuery plugin to check the text inside the viewport (demo)
Count the number of characters/WORDS with "Onscreen" label in the
viewport (a references)
Split the whole text in a list containing as many characters/WORDS as
there are in the "Onscreen" label
Create a section for each element of the obtained list, filling each
section with the relative text; the number of elements of the list
gives you the number of pages (sections) of the whole text. You may
navigate between sections like above
On resize event, redo [2-5] algorithm steps
Cheers
The idea is to have a div that will contain the whole text (let's call this div #epub_container). Then, you will have a div with the same size of the page viewport (let's call it #displayer) and it will contain #epub_container.
#displayer will have css overflow:hidden. So when the site loads, it will only show the first page, because the rest of the #epub_container will be hidden.
Then you need a page navigator to increment/decrement the page number. When the page number changes, we will move the top offset of the #epub_container based on that.
This is the jQuery function:
function move_to_page() {
var height = window.innerHeight;
var width = window.innerWidth;
var $displayer = $('#displayer');
var offset = $displayer.offset();
$displayer.height(height - offset.top - 5);
var $epub = $('#epub_container');
var offset_top = offset.top - $displayer.height() * m_page;
$epub.offset({top: offset_top, left: offset.left});
}
JSFiddle
EDIT: call move_to_page() after the text reflow in order to recompute the pages.
I created a plugin that handles this perfectly. It has features like dark mode, font changing, line height adjustment, select chapter in a side nav menu, save and restore scrolling/reading position. You can find it for free on git hub at https://github.com/E-TechDev/Html-Book-Reader
Screenshots
Light Mode Dark Mode Side Nav Menu Change Font Adjust Paragraph
You can try CSS scroll snap points on text with columns
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Scroll_Snap_Points
Somehow make the columns as wide as the viewport, and allow horizontal snapped scrolling.
Update
I mean to try to do the text flowing entirely using css. Pen:
https://codepen.io/ericc3141/pen/RYZEpr
body {
scroll-snap-type: mandatory;
scroll-snap-points-x: repeat(100%);
}
#columns-test {
height: 80vh;
columns: 90vw auto;
}
All you need is to insert a page break at the right places when you load the page. You can take the cue from here:
Dynamic Page-Break - Jquery
Here you can set the page height to your viewport height. You can handle the rest with javascript and css.

Preparing the DOM for a Single Page Application

What I am trying to achieve appears to be simple. I am assuming that all content visible on a page, is a child or grandchild of the tag of a HTML page? This text for example must be related to Body.
And this is a tree structure basically starting (visually) with Body.
I want to make a Single Page Application with JavaScript. But I need to prepare the DOM and here is where I struggle.
How do I set up the body of the page so that:
There is no scroll bars
The Body is always filling the entire content area of the browser (if it does not already)
Break out of CSS box model (appendChild 10 times stacks those elements, not flows them)
Any children Divs from Body, also by default break out of the CSS box model too.
I have searched for each step individually but I do not use JQuery, and to be honest, I would prefer to get rid of CSS too all but for the most basic of tasks. I would just like to have a known viewable region and be responsible for its positioning, sizing and content with JS.
Or. If you are similar with Flash. I want to treat the Body as my Stage and use it like a Display List with NO_SCALE enabled. Meaning that when you resize, that should "invalidate" the layout (that is upto me as the developer).
I am not the first person in the world to ask for this. So if you could even point me in the right direction, I would appreciate it.
Try CSS based solution to the fullest, then move on with Javascript. Following would give you pretty much what you want
There is no scroll bars
The Body is always filling the entire content area of the browser (if it does not already)
html, body {
margin : 0px
padding : 0px;
border : 0px;
overflow : hidden;
position : absolute;
left : 0px;
top : 0px;
}
Break out of CSS box model (appendChild 10 times stacks those elements, not flows them)
Any children Divs from Body, also by default break out of the CSS box model too.
put this first
* {
position : absolute;
}

jquery Mobile: can't set button's left and width?

Web app in progress is here. (corrected URL)
I'm trying to develop a music-reading quiz game for my daughters, using jQuery Mobile.
The part I'm having trouble with is the keyboard.
With "ordinary" html buttons, you can set the left/top/width/height css properties using jQuery's .css() method. But with jQuery Mobile, that doesn't seem to work. Anybody know how to move buttons in jQuery Mobile?
Here's an excerpt of the initial HTML for the keyboard buttons:
<div id="kbdC2E" class="keyboard">
<button class="pianoKey whiteKey" id="wk1" />
<button class="pianoKey blackKey" id="bk1" />
<button class="pianoKey whiteKey" id="wk2" />
<button class="pianoKey blackKey" id="bk2" /> <!-- etc. -->
And here's the javascript code to move them into position:
for (i = 0; i < numWhiteKeys; i++) {
var keyLeft = whiteKeyWidth * i;
var $key = $('button#wk' + i, $div).parent();
$key.addClass('pianoKey whiteKey')
.css({
// position: 'absolute', // from pianoKey class
left: keyLeft,
top: gap,
width: whiteKeyWidth,
height: whiteKeyHeight,
});
Note that we use the parent of the button#wk1 key, because jQuery Mobile has decorated our <button> HTML as:
<div data-theme="c" class="ui-btn ui-btn-corner-all ui-shadow ui-btn-up-c">
<span class="ui-btn-inner ui-btn-corner-all">...</span>
<button class="pianoKey whiteKey ui-btn-hidden" id="wk1"></button>
</div>
(You can verify the details by going to the web page and using a DOM/CSS inspector.)
When I run this, the result is that the parent div receives the added classes, by means of which it becomes position=relative; it also receives the top and height properties that I set:
<div data-theme="c" class="ui-btn ui-btn-up-c ui-btn-corner-all ui-shadow pianoKey whiteKey"
style="top: 72px; height: 90px; ">
<span class="ui-btn-inner ui-btn-corner-all">...</span>
<button class="pianoKey whiteKey ui-btn-hidden" id="wk1"></button>
</div>
However it does not receive the "left" and "width" properties that I set.
As a result, all the keys end up on the left side of the ancestor div, and they have the wrong width. Can anybody tell me how to effectively set the left and width properties?
Thanks...
P.S. update: For an illustration of what I want the result to look like, see here. This is not the mechanism I want to use, because by adding buttons at run time, it apparently bypasses JQM completely, thus losing the touch-friendly and cross-platform benefits I'm trying to get from JQM. But it shows the layout I want.
Have you tried adding the "px" to the width number? pretty much something like that:
.css({
// position: 'absolute', // from pianoKey class
left: keyLeft + 'px',
top: gap,
width: whiteKeyWidth +'px',
height: whiteKeyHeight +'px',
});
I'm assuming that whiteKeyHeight and whiteKeyWidth are well declared and you made sure of that using alert(). Also, on another note (oh what a funny joke here), you should not use position absolute this might cause some problems in different browser/resolution etc. You can use position relative to set the "left" attribute. Also, the float left seems to be the best answer since it all does that for you without the need to add any code but the class using the "float: left;" attribute.
This is not a direct answer to the question, as I belive that your values are just being rewritten due to the order in which scripts are being run, but
I suggest using div elements instead of buttons. Adding some classes to those divs will make them look as jquerymobile-ish as you want, and they don't need to be buttons at all... unless you want the application to be working without javascript too (I can't verify that at the moment)
[edit]
The second thing turned out a bit ambiguous... I'll elaborate.
I expect JQM to block or overwrite what you set. This can be avoided by reducing the ammount of JQM code that processes the buttons.
Inspect the buttins and copy most of their classes (this will give you the looks) and give those classes to some divs instead of button elements. Add your styles. Bind tap or click (I didn't see any bindings in your code at the moment) to get them to work - the same way you would have to do with buttons.
This is not a less-jqm-alike way to do that, you want to use relative positions and overlay buttons over other buttons - this is rather unusual, and so it needs unusual handling.
Another idea:
Have you seen this: http://jquerymobile.com/demos/1.0a3/#docs/buttons/buttons-grouped.html
or this: http://jquerymobile.com/demos/1.0a3/#docs/toolbars/docs-navbar.html
?
Maybe you don't have to position the buttons with position:relative at all (it's always a bit messy) just stick them together in two rows or stick pairs together as a column and set float:left on their wrappers. See what suits you
Well it's not functioning but is this what you wanted the key layout to do: http://jsfiddle.net/rg4wj/13/

Categories

Resources