The problem
I'm using innerHTML to put HTML-formatted text in a <div>. At a consistent, seemingly random point in the text, the fonts stop being rendered and display blank. When I select the invisible text, it reappears.
Description of the code
We have a single <div id="text" inner-h-t-m-l="[[markup]]">.
The initial markup doesn't contain any data apart from empty segments with IDs:
<div id="segment-1"></div><div id="segment-2"></div>... etc
In Javascript, we loop over the IDs using querySelector (this is slow) and insert HTML into the each segment's innerHTML.
The framework used is Polymer 2.
Additional info; video
In the Chrome Dev Tools, the invisible text is shown as present in the DOM and seems to be no different from the text that renders correctly.
The font in the video is non-standard, but the problem also occurs when using system fonts.
Here's a video to illustrate the problem.
Here's a screenshot of a Chrome profiler run:
Edit:
After a discussion in the comments, I thought I should link the actual code.
Here's the element in question.
The relevant parts are:
<div id="segmented_text_content" inner-h-t-m-l="[[markup]]"></div>
_addPrimaryText(textStrings) {...}
_addSecondaryText(textStrings) {...}
Edit 2:
I found two potential workarounds for this, but neither one works well enough.
If I run this.querySelector('#text').innerHTML = this.querySelector('#text').innerHTML with a timeout of 3 seconds, it paints the text correctly.
When adding the text, if I use the async processArray function from this comment, it renders the text correctly, albeit very slowly because it updates the layout after every insertion.
With these two points, my working theory now is that Chrome updates the layout before the innerHTML attribute is fully assigned.
I also forgot to mention this project uses Shady DOM.
Related
I've got what should be a fairly simple "please wait" overlay that should pop up during a variety of different tasks on a data-review/dashboard website I'm working on (data queries, chart rendering using ChartJS, putting data into a file for download, etc.) that is not consistently working as desired in Chrome.
I toggle the overlay via Javascript at the beginning and end of each function that I need it for. The overlay successfully displays/turns off on IE11 and Firefox every time I do a function that calls it, but on Chrome it is consistently inconsistent (ALWAYS displays during specific functions, NEVER displays during other functions) despite calling the same few lines of code every time. When it does not display, the console confirms that the method to toggle it is being called and I can see the page's HTML update, but the user's display does not change.
The functions that the overlay does not display for involve creating/updating ChartJS charts, including an AJAX call to get data. I have functions that the overlay displays properly on that involve just doing AJAX requests. On the functions where it does not display, it never shows up on the user's screen at all, even well before I get to ChartJS stuff. It's confusing as heck. I can give out a little of that code if it would be useful, but again since the overlay never even shows despite toggling the overlay as the first line of those functions, I don't think that's the the problem...
It's a style thing more than a function thing; I also disable other user-interface elements while the overlay is supposed to be active (which Chrome's display also does NOT reflect but again definitely occurs). But I'd still like the overlay to consistently display, particularly since those ChartJS functions can take a few seconds, and I'm wondering if people have any ideas.
HTML for overlay, as it appears on page-load
<div id="overlay">
<img src="img/loader.gif"></img><br><strong>Data loading, please wait</strong>
<div id="overlayAdditonalMesages">
</div>
</div>
Javascript for overlay toggle
function overlayToggleOn(additionalMsg) {
console.log("overlay should be on");
document.getElementById("overlay").innerHTML = '<img src="img/loader.gif"></img><br><strong>Data loading, please wait</strong>'+additionalMsg;
document.getElementById("overlay").setAttribute("style", "display: flex;");
$("overlay").show(); //just trying to fix the thing with Chrome
}
function overlayToggleOff() {
console.log("overlay should be off");
document.getElementById("overlay").innerHTML = '<img src="img/loader.gif"></img><br><strong>Data loading, please wait</strong>';
document.getElementById("overlay").removeAttribute("style");
}
Like I said, should be simple, and I struggle to understand why it's not working consistently in Chrome. Thoughts appreciated.
Why using .setAttribute() for you inline styles.
I also saw where you added a closing tag for your image element </img> why?
Use
id.style.property = value
Instead of setAttribute()
Is there a way to get information which scripts modified selected DOM element, and in which order?
On my website, I modify width of div A. It appears however, that some other script modifies that width after that, but I do not know which script it is. How can I find it?
Edit:
After searching a bit more, I fount that in firebug you can right click attribute in HTML view, and select "stop javascript on change" (or sth similar, my firefox is not in english), the problem being it resets after reloading the page, what makes it useles for me.
I am using chrome developer tools to debug my page. It supports add breakpoints to dom elements, when attributes of dom is modified by javascript, it breaks the rendering process immediately. I think you can try it.
There are so many issues with contenteditable divs and deleting html and/or non content editable content inside editable divs.
Using an answer by the excellent Tim Down here: How to delete an HTML element inside a div with attribute contentEditable?
Using Tim's code, the entire text node gets deleted. I need this to work like any textarea would, deleting character by character and just making sure html elements can be backspaced as well.
I tried the following
else if(node){
var index = node.length-1;
if(index >= 0)
node.deleteData(index,1);
else
this.removeChild(node);
}
But this is obviously not going to work correctly. If I am at the end of the content, things work as expected. But if I place the cursor anywhere else, it's still deleting from the end.
I'm lost at this point, any help is very appreciated
http://jsfiddle.net/mstefanko/DvhGd/1/
After breaking down how google uses contenteditable divs in their google plus user tagging, I landed on a much more reasonable solution. Maybe it will help someone else out.
After adding 1 tag, you can already see a lot of differences in the html browser to browser.
In Google Chrome, a space is added with each tag. The button tag is used. And the chrome-only contenteditable="plaintext-only" is used.
When I backspace the space in chrome, a BR tag is then appended.
In Firefox the BR tag is added immediately with the first tag. No spaces are needed. And an input tag is used instead of the button tag.
The BR tag was the single greatest break-through I had while digging through this. Before adding this, there was a lot of quirky behavior with deleting tags, as well as focus issues.
In IE, more interesting changes were made. A span with contenteditable false is used for the tags here. No spaces or BR tags, but an empty text node.
With all of that, you don't have to copy google exactly.
The important parts:
If you're rendering HTML, do the following...
1. Chrome should use the button tag
2. Firefox/IE should use the input tag
For range/selection you generally want to treat things like tags as a single character. You can build this into your range/selection logic, but the behavior of the input/button tags is much more consistent, and way less code.
IE behaves better in IE7-8 using a span. Just from a UI standpoint. But if you don't care if your site is pretty in old versions of IE, the input has the correct behaviour in IE as well as firefox.
3. Chrome only, use the contenteditable="plaintext-only" attribute on your editable div.
Otherwise, a lot of weird issues happen not only when a user tries to paste rich-text, but also when deleting html elements sometimes the styles can get transferred to the div, I noted many strange issues with this.
4. If you need to set the caret position to the end of the div, set the end of the range before the BR.
for FireFox:
range.setEndBefore($(el).find('br')[0]);
So I have this webpage that I want to copy to a word document. It's an installation guide and we want to use that but add comments relating to how we installed the program in our environment.
Simple problem. Just copy and paste, right? Wrong.
Problem is, this specific webpage is built up of <div ..> tags, where a couple of checkboxes enable/disable them relating to your choices. So I check the box that marks an installation in Linux, and all div tags relating to that installation option are shown.
Example from the source:
<div class="forWindows forAIX forLinux forZLinux forPLinux forSolaris">...</div>
<div class="forJTS"> ... </div>
<div class="forCCM"> ... </div>
This means, that whenever I copy and paste a part of the webpage I get all the content, regardless of what I actually see on the screen. What I want is to just copy the webpage as I see it on the screen.
I've tried to copy from Internet Explorer and Firefox both to MS Word and to a basic text editor with the same results.
I want the result to be text so I can edit it, so screenshots or exporting to PDF won't work.
I could save the source HTML, remove the tags that dont apply and open the local html file, except that it's quite alot of work. Also the page seems to rely heavily on scripts on the serverside, so I guess that may cause some issues.
Ideally I'd like to preserve the formatting as it is shown aswell.
To reproduce the issue:
Go to the IBM's interactive guide for installing Rational Team Concert.
Select any choices, but to verify step 5-6 below, choose Linux as OS.
Click "Get your interactions"
Copy/paste a part of the webpage and compare the pasted version with what is seen in the browser.
Go to Step 3, "set up the database" in the guide. Copy all the content between "What to do next" in the previous step to the end of the heading in step 3. All in all, about 6 lines.
Paste in a texteditor, you should now see text that only relates to zOS and IBMi operating systems.
It seems that the behaviour on copy and pasting is undefined. Some browsers will copy ignoring the styling that hides stuff and others will copy including styles (ie some will include hidden text and others will not).
A rough summary of browsers seems to be:
IE - copies hidden text on IE8 and presumably older, no idea about newer.
FF - newer versions will not copy hidden text, older versions it seems will. Unknown where the cutoff is but it seems to be somewhere between version 3 and version 14. :)
Chrome - my current version (19.0.1084.52) will copy just the visible text. Untested on any other version.
I would simply screenshot the page and use a simple graphics editing program to crop the image and add annotations.
To screenshot a page, press Print screen (probably shorted as PrtScn on your keyboard). That copies the screenshot into memory.
Now, in your graphics editing program or even word processor, click paste (or press ctrl-v). The screenshot will appear. Crop and add annotations as per your desire.
Write a bookmarklet that concatenates #text nodes together but only when the parent element has a computed-style where display != none and visiblity != hidden.
If you drag-and-drop around an image in Firefox in a contenteditable area, sometimes the images will end up being selected like this:
Fiddle here: http://jsfiddle.net/zupa/qg5Qh/
You may need to drag-drop it a few times, I have this bug in like 20+% of the time.
I am using Firefox 13.0.1 on Windows 7
How to remove that selection? Any help is appreciated.
Ps:
It is not available as a range via document.getselection().getRangeAt(..)
Firefox does NOT add any HTML attributes, still if I hit save (custom CMS), and reload the page in contenteditable mode, the selection comes back. Seems to be an annoying bug.
It does it reliably when the image is within a word that is marked by Firefox as a spelling error. For example, here's your jsFiddle with the image moved into the middle of the word "Lorem": http://jsfiddle.net/timdown/qg5Qh/1/
It seems to be something to do with the styling applied to misspelled words. Add the word "Lorem" to the browser's dictionary and the image styling goes away.
You could switch spellchecking off using the spellcheck attribute. From what I can gather, you have to do this at the <body> level in Firefox because it doesn't seem to work on single contenteditable elements as it does for textareas.
Demo: http://jsfiddle.net/timdown/qg5Qh/2/