CKeditor removes the image align attribute - javascript

I have a CMS which uses ckeditor at its heart. I have just upgraded the version of ckeditor to the latest and now when you align an image left or right in the editor it puts in the inline style rather than the 'align' attribute.
While the inline style is not a problem I need the 'align' attribute to remain so that I can apply padding to images through CSS programmatically without needing to add styles to each image in the editor (as the users of the CMS would not be technically competent to do this).
I have been successful in making a function to find images with the style attribute and assigning an align attribute. Then updating the editor using 'setData' and when I 'getData' the update seems to remain. However, at somepoint during the save process it seems to remove this. Any ideas on where this is, or how to add both align and style align at the same time.

After alot more Googling ironically I found the answer on here:
CKEditor align images instead of float
Why it didnt come up in searches I have no idea. This certainly did the trick, though I removed the lines relating to width and height and removed the replacement of the 'float' css attribute as this caused the WYSIWYG to not pickup the styling. Apart from that its all good!
UPDATE: I found there were instances where this didn't quite work with CKeditor 4 and found this small edition to the code fixed it.
element.forEach = function(){};
element.writeChildrenHtml = function(){};
See: http://vibhajadwani.wordpress.com/2011/07/18/how-to-remove-image-style-property-from-ckeditor/
So the complete code block is as follows:
CKEDITOR.on('instanceReady', function( ev )
{
// Ends self closing tags the HTML4 way, like <br>.
// See: https://stackoverflow.com/questions/4466185/ckeditor-align-images-instead-of-float
// Mod added for CKE 4
// See: http://vibhajadwani.wordpress.com/2011/07/18/how-to-remove-image-style-property-from-ckeditor/
ev.editor.dataProcessor.htmlFilter.addRules(
{
elements:
{
$: function( element )
{
// Output dimensions of images as width and height
if( element.name == 'img' )
{
var style = element.attributes.style;
if( style )
{
// Get the width from the style.
var match = /(?:^|\s)width\s*:\s*(\d+)px/i.exec( style ),
width = match && match[ 1 ];
// Get the height from the style.
match = /(?:^|\s)height\s*:\s*(\d+)px/i.exec( style );
var height = match && match[ 1 ];
// Get the float from the style.
match = /(?:^|\s)float\s*:\s*(\w+)/i.exec( style );
var align = match && match[ 1 ];
if( align )
{
element.attributes.align = align;
}
}
element.forEach = function(){};
element.writeChildrenHtml = function(){};
}
return element;
}
}
});
});

As a quick fix (for those of you who don't want to dig into the CKEditor files) you can actually style the images with CSS even if there is no align or class added to the image with the * property.
example:
img[style*=left] {
float: left;
margin: 0px 20px 10px 0px;
}
img[style*=right] {
float: right;
margin: 0px 0px 10px 20px;
}
You can use the [style*=] property to check the inline style="" attribute for the word 'left' or 'right' which gets applied to images by CKEditor, then style them any way you want. Clients will still use the alignment dropdown to select whether they want the image to float to the left or the right, so they won't notice any change at all.
We ran into the same issue as #Eth, however our entire ckEditor had been minified and was nearly uneditable. Hope this offers another solution!

Make sure filter is "Full HTML", Limit allowed HTML Tags is unchecked, Convert media tags to mark ups is checked and in Filter processing order Convert Media tags to markup is at top. Voila it is done

Related

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.

How to replace an any kind of element with a div of equal boxing?

I'm around trying to remove a DOM element (I'll put it elsewhere) and I need the position of the sibling elements do not change.
I tried some variations of this.
var elem = $("#theElement");
var ghost = $('<div></div>');
ghost.css({
width: elem.outerWidth(true),
height: elem.outerHeight(true),
margin: 0
});
elem.replaceWith(ghost);
But the document collapses slightly.
I know I can just change the visibility of the element, but not what I need. I'll put it somewhere else in the DOM and can not be duplicated.
The Question
How to replace any kind of element with a div that occupies the same space?
EDIT
Keep in mind that i can not change the source element attributes.
I do not know in advance which item and which properties it has, just take it out of where it is and move it elsewhere.
The jQuery documentation says:
.outerHeight(true): if the includeMargin argument is set to true, the margin (top and bottom) is also included.
.outerWidth(true): If includeMargin is omitted or false, the padding and border are included in the calculation; if true, the margin is also included.
plunker
That is because of the margin given by the browser, called user agent stylesheet in dev tools.
I have modified your plunk to have css like this
h1 {
color: red;
margin:0px !important;
}
Issue seemed to be resolved.
EDIT:
I have edited your code to be something like this:
$(function(){
var elem = $("h1");
var ghost = $('<div></div>');
ghost.css({
width: elem.outerWidth(),
height: elem.outerHeight(),
margin: 21
});
Since you can not modify the source, identify what styling the browser is putting onto it and give your ghost element the same styling.
To detect what css the browser is putting onto your element, refer
http://www.iecss.com
http://mxr.mozilla.org/mozilla-central/source/layout/style/html.css
http://trac.webkit.org/browser/trunk/Source/WebCore/css/html.css

Javascript/jQuery get true width and position of float affected element?

Is it possible to get the width (using javascript or jQuery) of a float-affected element? When text is being pushed over due to a floating image is it possible to get its position and true width? I have attached an image to explain better.
Code example,
<div>
<img style="...float: left"/>
<h1>A title!</h1>
<p>Text!</p>
<h1>New header added.</h1>
</div>
Picture
I need to find the width starting from the arrow, (the gray box is the image)(the dotted line is the width according to Firefox inspect mode).
I would like to avoid changing all the elements display types if possible.
Thank you!
I'm a little late to the party, but I had a similar problem and came up with a solution which (so far) seems to work in all instances of this issue. I like this solution because as far as I can tell, it works independent of the floating element - all you need is the element whose true width/position you want to get, nothing more. I've done it in pure Javascript for speed purposes, but it can easily be streamlined with jQuery and a separate CSS Stylesheet if you so choose.
//Get the rendered bounding box for the content of any HTMLElement "el"
var getLimits = function(el) {
//Set a universal style for both tester spans; use "!important" to make sure other styles don't mess things up!
var testerStyle = 'width: 0px!important; overflow: hidden!important; color: transparent!important;';
//Create a 'tester' span and place it BEFORE the content
var testerStart = document.createElement('SPAN');
testerStart.innerHTML = '|';
var testerFloat = ' float: left!important;';
testerStart.setAttribute('style', testerStyle + testerFloat);
//Insert testerStart before the first child of our element
if (el.firstChild) {
el.insertBefore(testerStart, el.firstChild);
} else {
el.appendChild(testerStart);
}
//Create a 'tester' span and place it AFTER the content
var testerEnd = document.createElement('SPAN');
testerEnd.innerHTML = '|';
testerFloat = ' float: right!important;';
testerEnd.setAttribute('style', testerStyle + testerFloat);
el.appendChild(testerEnd);
//Measure the testers
var limits = {
top: testerStart.offsetTop,
bottom: testerEnd.offsetTop + testerEnd.offsetHeight,
left: testerStart.offsetLeft,
right: testerEnd.offsetLeft
}
//Remove the testers and return
el.removeChild(testerStart);
el.removeChild(testerEnd);
return limits;
};
So, in your case, the code would just be:
var paragraphBoundingBox = getLimits($('div>p').get(0));
A couple things to note:
1) The float direction would be reversed if you are using an RTL language
2) All of the four edge positions in the output object are relative to the el.offsetParent - use this handy function can find their positions relative to the document.
First of all, the "full width" is exactly the true width.
You can watch this picture, it can help you understand why the true width and true position of the affected element is the way firefox tells you.
http://i.stack.imgur.com/mB5Ds.png
To get the width of inline text where it's pushed right by the float image, there's no good way except using the full width minus the float image's width.
var w = $('p').width()
- $('img').width()
- $('img').css('margin-left').replace("px", "")
- $('img').css('margin-right').replace("px", "")
- $('img').css('padding-left').replace("px", "")
- $('img').css('padding-right').replace("px", "")
- $('img').css('border-left-width').replace("px", "")
- $('img').css('border-right-width').replace("px", "");

Insert a logo next to any DOM element

I'm developing a Firefox extension which amends the contents of a loaded webpage. First I select all the elements of which the "src" or "href" attributes match my regex (this part of the code works).
Then, I would like to place a little image at the top right corner of the found element's parent using the following code:
/* create and add attributes to image */
var img = window.content.document.createElement("img");
var b = window.content.document.createAttribute("src");
b.nodeValue = "chrome://plugin/content/agent.png";
img.setAttributeNode(b);
img.addEventListener("click", function(){ alert("ds"); });
img.style.display = "block";
img.style.border = "3px solid red";
img.style.position = "relative";
img.style.top = "-10px";
img.style.right = "-10px";
img.style.left = "20px";
// ... the code to return the element...
//now insert the image
$jq(img).appendTo(element.parentNode);
The current result is that either the image is shown just at the bottom of the element's parent or not shown at all.
If you look at this: http://jsfiddle.net/yzwh5/64/ - I would like my button to work in a similar manner to that red cross.
You must "play" with the element's CSS positioning, in fact it doesn't matter where do you insert the images, but where you do position them.
Maybe you would like to take a look at "next-to", a jQuery plugin that automates the calculations to position an element next to another element
For example:
<script type="text/javascript">
$('.PlaceThisDiv').nextTo($('.ThisOtherDiv'), {position:'right', shareBorder:'top'});
</script>
As you can see in this Fiddle i have prepared (contains the plugin itself)
http://jsfiddle.net/PvcNr/
you will get you something like this:
More info: https://code.google.com/p/next-to/
Hope it helps
Try CSS code like this:
.my-ext-overlay:after {
content:url(smiley.gif);
position: absolute;
margin-left: -16px; margin-top: -16px;
}
and then adding the ".my-ext-overlay" class name to each element you find.
See example
Firstly, CSS floats are called cssFloat (or htmlFloat in some browsers) because float is a reserved word. Second, there is no such float value as block.
Third, you missed an x in -10px for the right property.
Fourth, setting both relative left and right positions can lead to unexpected behaviour.
Fifth, you shouldn't use createAttribute, since attribute nodes aren't reliable in all browsers. Instead, use setAttribute on the element.
Sixth, if this did work it would mess up page layout around the element you're searching for, so you would be better off with position: absolute so it doesn't affect the flow. If you do this, however, you should use margin-left instead of left (same for other directions), to shift the element around.
I think that should at least get the thing close to working...

Find the "potential" width of a hidden element

I'm currently extending the lavalamp plugin to work on dropdown menus but I've encountered a small problem. I need to know the offsetWidth of an element that is hidden. Now clearly this question makes no sense, rather what I'm looking for is the offsetWidth of the element were it not hidden.
Is the solution to show it, grab the width, then hide again? There must be a better way...
The width of an element that has CSS visibility: hidden is measurable. It's only when it's display: none that it's not rendered at all. So if it's certain the elements are going to be absolutely-positioned (so they don't cause a layout change when displayed), simply use css('visibility', 'hidden') to hide your element instead of hide() and you should be OK measuring the width.
Otherwise, yes, show-measure-hide does work.
The only thing I can think of is to show it (or a clone of it) to allow retrieval of the offsetWidth.
For this measurement step, just make its position absolute and its x or y value a big negative, so it will render but not be visible to the user.
You can use the following function to get the outer width of an element that is inside a hidden container.
$.fn.getHiddenOffsetWidth = function () {
// save a reference to a cloned element that can be measured
var $hiddenElement = $(this).clone().appendTo('body');
// calculate the width of the clone
var width = $hiddenElement.outerWidth();
// remove the clone from the DOM
$hiddenElement.remove();
return width;
};
You can change .outerWidth() to .offsetWidth() for your situation.
The function first clones the element, copying it to a place where it will be visible. It then retrieves the offset width and finally removes the clone. The following snippet illustrates a situation where this function would be perfect:
<style>
.container-inner {
display: none;
}
.measure-me {
width: 120px;
}
</style>
<div class="container-outer">
<div class="container-inner">
<div class="measure-me"></div>
</div>
</div>
Please be aware that if there is CSS applied to the element that changes the width of the element that won't be applied if it's a direct descendant of body, then this method won't work. So something like this will mean that the function doesn't work:
.container-outer .measure-me {
width: 100px;
}
You'll either need to:
change the specificity of the CSS selector ie. .measure-me { width: 100px; }
change the appendTo() to add the clone to a place where your CSS will also be applied to the clone. Ensure that where ever you do put it, that the element will be visible: .appendTo('.container-outer')
Again, this function assumes that the element is only hidden because it's inside a hidden container. If the element itself is display:none, you can simply add some code to make the clone visible before you retrieve it's offset width. Something like this:
$.fn.getHiddenOffsetWidth = function () {
var hiddenElement $(this)
width = 0;
// make the element measurable
hiddenElement.show();
// calculate the width of the element
width = hiddenElement.outerWidth();
// hide the element again
hiddenElement.hide();
return width;
}
This would work in a situation like this:
<style>
.measure-me {
display: none;
width: 120px;
}
</style>
<div class="container">
<div class="measure-me"></div>
</div>
Two options:
position the element outside the viewport (ex: left:-10000px)
use visibility: hidden or opacity: 0 instead of hide().
Either way will work as hiding the element but still being able to get the computed width. Be careful with Safari on thi, it's awfully fast and sometimes too fast...
Actual jQuery plugin!
Usage:
console.log('width without actual: ' + $('#hidden').width());
console.log('width with actual: ' + $('#hidden').actual('width'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.actual/1.0.19/jquery.actual.min.js"></script>
<div style="width: 100px; display: none;">
<div id="hidden"></div>
</div>
If you know the element to be the full width of a parent element another approach is to create a recursive method:
es5:
var getWidth;
getWidth = function($el){
return $el.offsetWidth || getWidth($el.parentElement);
}
var width = getWidth(document.getElementById('the-element'));
es6:
let getWidth
getWidth = ($el) => $el.offsetWidth || getWidth($el.parentElement)
const width = getWidth(document.getElementById('the-element'))
What I did was ;
by the time hiding that element, stored its width in its dataset.
It only will work for you if you can hide programmatically.
ie.
When Hiding ;
var elem = $("selectorOfElement");
elem.dataset.orgWidth = elem.clientWidth;
Later when getting ;
var elem = $("selectorOfElement");
var originalWidthWas = elem.dataset.orgWidth;
thats because its hidden via display: none; What ive done in the past is to make a "reciever" div which i use absolute positioning on to get it off the page. Then i load the new element into that, grab the dimensions and then remove it when im done - then remove the reciever when im done.
Another thing you can do is to not use hide(); but to instead set visibility: hidden; display: ; However this means the blank area will be rendered wherever the node is attached.
var $hiddenElement = $('#id_of_your_item').clone().css({ left: -10000, top: -10000, position: 'absolute', display: 'inline', visibility: 'visible' }).appendTo('body');
var width = parseInt($hiddenElement.outerWidth());
$hiddenElement.remove();
I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.
flexibox example
I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.
Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.
$('[selector]').onVisibleChanged(function(e, isVisible)
{
var realWidth = $('[selector]').width();
var realHeight = $('[selector]').height();
// render or adjust something
});
For more information, Please visit at my project GitHub.
https://github.com/Soul-Master/visible.event.js
demo: http://jsbin.com/ETiGIre/7
Sorry I am late to this conversation. I am surprised no one has mentioned getComputedStyle. (Note this only works if the CSS sets a width value)
Grab the element:
let yourEle = document.getElementById('this-ele-id');
and use the function:
getComputedStyle(yourEle).width
This returns a string so you will have to remove the numbers from the string.
This works even when the element's display style is set to none.
Other articles to read about this includes here at zellwk.com

Categories

Resources