Changing div text layouts the whole document - javascript

I want to change the text inside a div with javascript (jQuery is ok too).
There are a few to do that:
element.innerText
element.innerHTML
element.textContent
$(element).text()
$(element).html()
But when I use the above methods, the whole document is affected and not only the div.
See chrome timeline below which refers to this fiddle
Is there a way to update the text inside the div without affecting the whole document?

Because updating the text affects the width/height of the element and the flow of the page, the entire document usually has to be laid out again whenever the DOM changes. However, you can do stuff so only part of the document needs to be re-laid out.
From http://wilsonpage.co.uk/introducing-layout-boundaries/
To be a layout boundary, the element must:
Be an SVG root (<svg>).
Be a text or search <input> field.
or:
Not be display inline or inline-block
Not have a percentage height value.
Not have an implicit or auto height value.
Not have an implicit or auto width value.
Have an explicit overflow value (scroll, auto or hidden).
Not be a descendant of a table element.
If you make the element you're updating a layout boundry, only the part of the document inside your element needs to be updated. However, keep in mind were talking about optimizations of less than a millisecond, and pre-mature optimizations are generally considered bad practice.

Note: Do not start an id attribute with a number. It may cause problems in some browsers.
http://www.w3schools.com/jquery/sel_id.asp

Related

What are the conditions under which a browser will re-assess and reapply CSS selectors and styles?

Talking strictly about CSS 3, html 5, and styles defined on stylesheets or in a top level tag:
This is what I have observed:
Using JavaScript:
If I move an element under another element, or out from under its parent, it and all elements under it will be reviewed for restyling.
If I add or remove a class, an element and all elements under it will be reviewed for restyling.
If I add or remove an attribute, an element and all elements under it will be reviewed for restyling.
I assume this is also true of the sibling (~) relationship as well? I mean moving elements relative to their siblings?
Is there anything else that would trigger this? And is there a document someplace (like mozilla) that specifies this as a standard?
That really depends on what you call "restyling".
Here it seems you are only talking about recalculating all the styles applied by all the stylesheets in the document, which is also known as "reflow" or "layout".
Any change to the DOM will mark the CSSOM as dirty, and before the time of rendering, the browser will perform such a reflow if the CSSOM is dirty.
Note that some operations, (most being referenced in this gist by Paul Irish), will force a synchronous reflow, because they do require that an up-to-date boxing model is calculated to return the correct values, or to act correctly. So one must be careful when doing DOM changes in a loop to not also force such a synchronous reflow and let the browser do it at the best time, (generally just before the next paint, but in some browsers it's also done at idle).
However this reflow may not cost much, browser may very well have enough optimizations to know what could have changed and go only through these. Moreover, these don't include the repaint operation, which will only happen at the next screen refresh.
But this is not the only time the browser needs to recalculate the boxes layout, for instance every time the page is resized, or even if an in-flow element's size changes as part of an animation etc. but here, the full stylesheets are not recomputed.
There is not really a place in the specs where it is defined what should trigger these operations, nor even when they should occur. The HTML specification only asks that the browser performs the "rendering steps" of the event-loop, which does end with a quite underspecified "update the rendering or user interface of that Document". The ResizeObserver API does extend the rendering step to include styles recalc and layout update but they don't go as far as defining these steps. Note that there is an open issue on the HTML specs to define it more clearly, but for the time being we don't even have browser interoperability.
CSS3 is a series of separate specifications covering individual topics but includes CSS2 within the official definition of CSS.
CSS2 definitions define rendered content as (the emphasis mine)
The content of an element after the rendering that applies to it according to the relevant style sheets has been applied. How a replaced element's content is rendered is not defined by this specification. Rendered content may also be alternate text for an element (e.g., the value of the XHTML "alt" attribute), and may include items inserted implicitly or explicitly by the style sheet, such as bullets, numbering, etc.
I take this to mean that when a document or part thereof is rendered, the browser is responsible for ensuring that CSS rules are correctly applied according to the content rendered, with DOM content and CSS rules specifiying the logical requirements of the rendering and the browser ensuring it produces page layout in conformance with the logical model.
My experience is that browsers will update the DOM when vanilla JavaScript1 code adds, moves or removes elements in the DOM, and also when changes to style sheet content or element style attributes are updated in script. Changes in the DOM appear to take place synchronously when modifying element placement or styling rules: getting the bounding rectangles of elements in the DOM whose styling or location in the DOM has been modified does not require browses to render the content first.
Hence you're basic assumptions about what you can rely on are essentially correct apart from the wording
reviewed for restyling
There is no active "review" taking place - excepting that modifying some properties can trigger a automatic and synchronous reflow operation in order for the calling script to have synchronous access to DOM property values that would need a reflow to calculate.
If you change the position of an element in the DOM, it's position has been changed upon return from the method used to change it's position.
If you add, delete or modify an attribute of an element (in the DOM), changes to the attribute and any side effects produced will have been put into effect upon return from the method used to make the change.
If you add, delete or modify style sheet rules, changes are effective in results returned from DOM inspection after the method used to change the style sheet returns.
There is a hint of this in the MDN article on `Window.getCommputedStyle. Overall I don't think it's mentioned anywhere specifically because it's inherent in the behavior of the DOM. You only get to know about it when a) you need to use the results of changes synchronously and b) are curious as to why it works as you hoped it would! :)
Examples showing synchronous results:
Changing element location in the DOM, style attribute changes:
setTimeout( ()=> {
const [div1, div2] = Array.from(document.querySelectorAll('div'));
div2.appendChild(div1);
div1.style.backgroundColor = "yellow";
div1.style.textAlign = "center";
const rect = div1.getBoundingClientRect();
const style = getComputedStyle(div1);
console.log("Synchronous results for div1: ",
{top: rect.top, backgroundColor: style.backgroundColor});
}, 3000);
<div>Division 1</div>
<div>Division 2</div>
... please wait 3 seconds
Changing CSS rules in the CSSOM
"use strict";
let div = document.querySelector('div');
let sheet = Array.from(document.styleSheets)
.find(sheet=> sheet.ownerNode.id == "absDiv");
console.log("div offsetWidth ", div.offsetWidth); // before
sheet.insertRule("div{ width: 200px;}");
console.log("after width set in CSS: ", div.offsetWidth);
<style id="absDiv">
div { background-color: yellow; }
</style>
<div>Div element</div>
1 Vanilla Javascript implicitly excludes cases of shadow DOMs and components. Refer to Kaido's answer for broader treatment.

Saving off an element's CSS and reapply later

Is it possible to save off all CSS that is 'currently' applied to an element, then later reapply it? I am working on a sticking table header, and when I i change position:fixed it loses all the applied styles. I currently save off the column widths and reapply to the table header with:
$('#tableHeader').css({
position:'fixed',
width:$('#tablePanel').width(),
top:$('#top').height(),
});
$('.column1Value').width(col1Width);
$('#col1').width(col1Width);
$('.column2Value').width(col2Width);
$('#col2').width(col2Width);
$('.column3Value').width(col3Width);
$('#col3').width(col3Width);
$('.column4Value').width(col4Width);
$('#col4').width(col4Width);
$('.column5Value').width(col5Width);
$('#col5').width(col5Width);
$('.column6Value').width(col6Width);
$('#col6').width(col6Width);
$('.column7Value').width(col7Width);
$('#col7').width(col7Width);
This make the columns the correct size and line up closely, but there is extra padding or margin being applied from somewhere I can't completely figure out (bootstrap probably), and this makes the headers and columns not line up. I was hoping for something like:
var savedCSS = $('#table').css(); and retrieve it like $('#table').css(savedCSS)
You could save off the individual styles that you are interested in one by one and then re-apply them later using the jQuery("selector").css("styleName") method that you alluded to, but I don't think there's an easy way to do them all at once. It's not impossible, but wouldn't be very efficient and probably wouldn't actually give you the result you want, once the element is in its new position.
After the discussion, we found that the sizing issue wasn't really due to the styles, but due to the element that the width was being calculated from.
When the element is positioned normally in the page-flow, it uses its most recent positioned parent's width and then takes off margin to find the width of the child content.
When the element is removed from the page flow, its width is then independent of the parent. So to get the two to match up, record the parent's width rather than the element itself and set the width to match the parent, instead of trying to maintain the element's width.

Is it possible to get only hidden text of an element?

Selenium Webdriver contains a function that returns only visible text inside element. I'd want to write a function that will get only hidden text inside element (i.e. all text that isn't visible in meaning of Selenium Webdriver W3C spec). According to to this spec element is visible only if all following conditions are met:
The element must have a height and width greater than 0px.
The element must not be visible if that element, or any of its ancestors, is hidden or has a CSS display property that is none.
The element must not be visible if there is a CSS3 Transform property that moves the element out of the viewport and can not be scrolled to.
OPTIONs and OPTGROUP elements are treated as special cases, they are considered shown if and only if the enclosing select element is visible.
MAP elements are shown if and only if the image it uses is visible. Areas within a map are shown if the enclosing MAP is visible.
Any INPUT elements of "type=hidden" are not visible
Any NOSCRIPT elements must not be visible if Javascript is enabled.
The element must not be visible if any ancestor in the element's transitive closure of offsetParents has a fixed size, and has the CSS style of "overflow:hidden", and the element's location is not within the fixed size of the parent.
Is it possible to write a JS function that will return only hidden text contained inside element? Do you know of any library that contains such function? How slow will such function be?
Yes, it is possible to write such code if you are just monitoring for display: none, visibility: hidden and no size or even an absolute/relative position that is off the screen. You would have to iterate every element in the page, determine if the element is visible and, if so, collect the text from any text nodes in that element.
It will be no slower or faster than any other function that iterates every node in the document. The total time will depend upon how efficiently the iteration code is written (it can skip all children of a hidden element for example) and on how long/complicated the document is.
If you want to be able to tell the difference between text that is outside the edges of an element with overflow:hidden or elements that might be marked for visibility, but be off-screen or out of view or out of the current viewable scroll area or pieces of text that might be obscured by other elements, that would be very difficult and honestly I don't know if all of that can be figured out from pure javascript.

Width of an inline element

How do know width of an inline element, without adding to document?
With adding
var span = document.createElement('span');
span.innerHTML = 'Hello, world!';
span.offsetWidth; //0
document.body.appendChild(span);
span.offsetWidth; //70
How without adding to document?
Sorry for my english)
The width of an element does obviously depend on the styles used (e.g. on the font size), so it is impossible to compute the width of the element without knowing where it is in the DOM.
You may add it to some invisible element if you don't want it to show on the screen.
You cannot get a width of an element if the element itself is not part of the DOM.
You need to append it, but you may also position it outside the visible area (with position: absolute and a negative left/top property) and remove it once you got the width
Until the element is added, there's no way to know for sure how wide it is, because it depends on the styling context.
jQuery's width() method has a trick that it uses for display: none elements, I don't know if it will work for an element that hasn't even been added to the DOM (it works by temporarily showing the element, getting the width, then hiding it again).

How to check in jQuery is a table cell overflow in width?

In other words, is there a way to get width of cell text in spite of overflowing?
There's a concept of a 'text node', but frankly I don't know how to access its width. My first instinct is if it's a must-have, wrap the cell contents up in a span and get the span's width. Strikes me as kind of ugly, but sometimes purpose is more important than appearance. ;-)
$('td span').width(); // the width
Fiddle: http://jsfiddle.net/Cvv4b/
If you really need to avoid the span, you could write a wee little utility that gets the text, copies it into a hidden element that doesn't have a width restriction (this element could be created on the fly if need be), and return the width of that hidden element.

Categories