Does CKEDITOR.htmlParser.element.remove() remove the element and its descendants or does it just remove the element and stuff its immediate children into its place? I ask because I've tried a number of approaches to removing elements from consideration during a filter and have wound up with not quite the results I expect; removing the element seems not to remove everything it ought to, but perhaps it's intended behavior.
If the latter, what is the most efficient way to remove an element and everything inside it? setHtml()?
The CKEDITOR.htmlParser.node.remove removes the node which means also its children are removed.
// Removing `em` element on `setData`.
CKEDITOR.instances.editor1.on( 'toHtml', function( evt ) {
var ems = evt.data.dataValue.find( 'em', true );
console.log( 'Before: ' + evt.data.dataValue.getHtml() );
if ( ems.length ) {
ems[ 0 ].remove();
}
console.log( 'After: ' + evt.data.dataValue.getHtml() );
}, null, null, 14 );
See this codepen demo.
It is hard to speculate on your case without seeing any code, but I assume you are trying to manipulate some copy of the element or partial tree which is not used any further so it may seem this method does not work as expected.
If you provide the code, it will be easier to check if there are any issues with it.
Related
I got a tree of elements and each element got a toggle icon to expand it -My intention is to click on the toggle icon corresponding to the element have a text for ex "TIME PERIODS"
Currently i write my code like below , Is there a better way to do this?
Please see the screenshot for my element structure.
cy.get('.tree-node',{ timeout: 60000 }).contains('TIME PERIODS',{force: true}).parent().parent().find('.tree-node-collapsed').click()
each() method is available in Cypress.io. Using which we can travell through tree of elements and can filter using text. Please follow below code approach:
Code
cy
.get('.tree-node')
.each(($el, index, $list) => {
// $el is a wrapped jQuery element
$el.get('.tree-item').contains('TIME PERIODS').siblings('.tree-node-
collapsed').click();
});
I have fixed issues -working code given below
cy.get('.tree-node').each(($el, index, $list) => {
// $el is a wrapped jQuery element
cy.wrap($el).get('.tree-item').contains('TIME PERIODS').parent().siblings('.tree-node-collapsed').click();
We can do like shown below also with out using .each
cy.get('.tree-node').get('.tree-item').contains('Header').parent().siblings('.tree-node-collapsed').click();
Here's the how I append data to struct:
user.Things = append(user.Things, item.Id)
Now, how can I remove item.id from user.Things? Seems like there's no method like delete, remove, or similar.
For example, this didn't work:
user.Things = append(user.Things[:item.id], user.Things[:item.id + 1:])
The wiki page Slice tricks gives a good overview about operations on slices.
There is also a few ways to delete an elements a slice: cutting, deleting or deleting without preserving order.
In your case, it seems you just had a typo (an extra colon):
user.Things = append(user.Things[:item.id], user.Things[item.id + 1:])
I'm facing an issue with CKEditor 4, I need to have an output without any html entity so I added config.entities = false; in my config, but some appear when
an inline tag is inserted: the space before is replaced with
text is pasted: every space is replaced with even with config.forcePasteAsPlainText = true;
You can check that on any demo by typing
test test
eg.
Do you know how I can prevent this behaviour?
Thanks!
Based on Reinmars accepted answer and the Entities plugin I created a small plugin with an HTML filter which removes redundant entities. The regular expression could be improved to suit other situations, so please edit this answer.
/*
* Remove entities which were inserted ie. when removing a space and
* immediately inputting a space.
*
* NB: We could also set config.basicEntities to false, but this is stongly
* adviced against since this also does not turn ie. < into <.
* #link http://stackoverflow.com/a/16468264/328272
*
* Based on StackOverflow answer.
* #link http://stackoverflow.com/a/14549010/328272
*/
CKEDITOR.plugins.add('removeRedundantNBSP', {
afterInit: function(editor) {
var config = editor.config,
dataProcessor = editor.dataProcessor,
htmlFilter = dataProcessor && dataProcessor.htmlFilter;
if (htmlFilter) {
htmlFilter.addRules({
text: function(text) {
return text.replace(/(\w) /g, '$1 ');
}
}, {
applyToAll: true,
excludeNestedEditable: true
});
}
}
});
These entities:
// Base HTML entities.
var htmlbase = 'nbsp,gt,lt,amp';
Are an exception. To get rid of them you can set basicEntities: false. But as docs mention this is an insecure setting. So if you only want to remove , then I should just use regexp on output data (e.g. by adding listener for #getData) or, if you want to be more precise, add your own rule to htmlFilter just like entities plugin does here.
Remove all but not <tag> </tag> with Javascript Regexp
This is especially helpful with CKEditor as it creates lines like <p> </p>, which you might want to keep.
Background: I first tried to make a one-liner Javascript using lookaround assertions. It seems you can't chain them, at least not yet. My first approach was unsuccesful:
return text.replace(/(?<!\>) (?!<\/)/gi, " ")
// Removes but not <p> </p>
// It works, but does not remove `<p> blah </p>`.
Here is my updated working one-liner code:
return text.replace(/(?<!\>\s.)( (?!<\/)|(?<!\>) <\/p>)/gi, " ")
This works as intended. You can test it here.
However, this is a shady practise as lookarounds are not fully supported by some browsers.
Read more about Assertions.
What I ended up using in my production code:
I ended up doing a bit hacky approach with multiple replace(). This should work on all browsers.
.trim() // Remove whitespaces
.replace(/\u00a0/g, " ") // Remove unicode non-breaking space
.replace(/((<\w+>)\s*( )\s*(<\/\w+>))/gi, "$2<!--BOOM-->$4") // Replace empty nbsp tags with BOOM
.replace(/ /gi, " ") // remove all
.replace(/((<\w+>)\s*(<!--BOOM-->)\s*(<\/\w+>))/gi, "$2 $4") // Replace BOOM back to empty tags
If you have a better suggestion, I would be happy to hear 😊.
I needed to change the regular expression Imeus sent, in my case, I use TYPO3 and needed to edit the backend editor. This one didn't work. Maybe it can help another one that has the same problem :)
return text.replace(/ /g, ' ');
I'm working on a jsPlumb problem. When I try to programatically remove all of an elements connections, I get Uncaught TypeError: Cannot read property 'left' of undefined
I have several "nodes" (html elements) that each have 1 input endpoint (an end point that accepts) and n output endpoints. Each node also has a javascript object behind it. I have a "selected" state in my software. Users can select multiple nodes and the objects are pushed to an array called selected. I have a key listener for the delete key. When the key is pressed it loops through selected nodes, deleting them and removing their endpoints. This works great when there are no connections, but when there are connections I the error.
The output endpoints are attached to child HTML elements of the main node…
There's a lot of code doing lots of stuff, but I'll try to share the relevant parts:
function Node(jsonFromServer){
/* … this is the constructor method… some code omitted*/
this.endpoints = [];
this.endpoints.push(jsPlumb.addEndpoint(this.el.attr("id"),targetEndpoint,{anchor:"TopCenter",uuid: this.el.attr("id") + "TopCenter"}));
this.addConnectionEndpoints();
}
Node.prototype.addConnectionEndPoints = function(){
//omitting code… loops through 'connections' that don't have 'has endpoint' marked….
this.endpoints.push(jsPlumb.addEndpoint(connection['el'].attr("id"),sourceEndpoint,{anchor:"RightMiddle",uuid:connection['el'].attr("id")+"RightMiddle"}));
connection.hasEndPoint = true;
}
So that was the setup. Here's how I delete
when key pressed
If key is delete /* all this stuff works */
loop through selected array (the array of selected Node elements:works)
node.el.hide(250).remove();
loop through node's endpoints array
//endpoint is the correct object... proved with console.log
//the following line is the error
jsPlumb.deleteEndpoint(endpoint);
ajax call to server to delete stuff on the backend
Fixed it! When I said node.el.hide(250).remove(); I removed an html element that was required in the delete process. I moved the remove part to the callback of the ajax request, and it works like a charm.
I have read a number of posts about this but none with any solid answer. Here is my code:
// button creation
onew = document.createElement('input');
onew.setAttribute("type", "button");
onew.setAttribute("value", "hosts");
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
onew.setAttribute("onclick", "fnDisplay_Computers('" + alines[i] + "')"); // mozilla
odiv.appendChild(onew);
Now, the setAttribute() method (with the mozilla comment) works fine in mozilla but only if it comes AFTER the line above it. So in other words it seems to just default to whichever gets set last. The .onclick method (with the ie comment) does not work in either case, am I using it incorrectly?
Either way I can't find a way to make this work at all in IE, let alone in both. I did change the function call when using the .onclick method and it worked fine using just a simple call to an alert function which is why I believe my syntax is incorrect.
Long story short, I can't get the onclick parameter to work consistently between IE/Mozilla.
-- Nicholas
onew.setAttribute("type", "button");
Never use setAttribute on HTML documents. IE gets it badly wrong in many cases, and the DOM-HTML properties are shorter, faster and easier to read:
onew.type= 'button';
onew.onclick = function(){fnDisplay_Computers("'" + alines[i] + "'"); }; // ie
What is ‘alines’? Why are you converting it to a string and surrounding it with single quotes? It looks like you are trying to do something heinous involving evaluating code in a string (which is what you're doing below in the ‘onew.setAttribute’ version). Evaluating JavaScript code in strings is almost always the Wrong Thing; avoid it like the plague. In the above case, IE should do the same as Firefox: it shouldn't work.
If ‘alines[i]’ is a string, I guess what you're trying to do is make it remember that string by constructing a code string that will evaluate in JavaScript to the original string. But:
"'" + alines[i] + "'"
is insufficient. What happens if ‘alines[i]’ has an apostrophe in, or a backslash?
'O'Reilly'
you've got a syntax error and possible security hole. Now, you could do something laborious and annoying like:
"'" + alines[i].split('\\').join('\\\\').split("'").join("\\'") + "'"
to try to escape the string, but it's ugly and won't work for other datatypes. You could even ask JavaScript to do it for you:
uneval(alines[i])
But not all objects can even be converted to evaluatable JavaScript source strings; basically the entire approach is doomed to failure.
The normal thing to do if you just want to have the onclick callback call a function with a parameter is to write the code in the straightforward way:
onew.onclick= function() {
fnDisplay_Computers(alines[i]);
};
Generally this will work and is what you want. There is, however, a slight wrinkle which you may have hit here, which could be what is confusing you into considering the wacky approach with the strings.
Namely, if ‘i’ in this case is the variable of an enclosing ‘for’ loop, the reference to ‘alines[i]’ won't do what you think it does. The ‘i’ will be accessed by the callback function when the click happens — which is after the loop has finished. At this point the ‘i’ variable will be left with whatever value it had at the end of the loop, so ‘alines[i]’ will always be the last element of ‘alines’, regardless of which ‘onew’ was clicked.
(See eg. How to fix closure problem in ActionScript 3 (AS3) for some discussion of this. It's one of the biggest causes of confusion with closures in both JavaScript and Python, and should really be fixed at a language level some day.)
You can get around the loop problem by encapsulating the closure in its own function, like this:
function callbackWithArgs(f, args) {
return function() { f.apply(window, args); }
}
// ...
onew.onclick= callbackWithArgs(fnDisplay_Computers, [alines[i]]);
And in a later version of JavaScript, you'll be able to say simply:
onew.onclick= fnDisplay_Computers.bind(window, alines[i]);
If you would like to be able to use ‘Function.bind()’ in browsers today, you can get an implementation from the Prototype framework, or just use:
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner,
args.length===0? arguments : arguments.length===0? args :
args.concat(Array.prototype.slice.call(arguments, 0))
);
};
};
}
I usually use something like:
onew.onclick = new Function("fnDisplay_Computers('" + alines[i] + "')");
this should work both in IE e Firefox.
Use the addEventListener() function with "click" for the type argument for Mozilla-based browsers, and attachEvent() function with "onclick" as the sEvent argument for IE; I find it best to use a try/catch statement, for example:
try {
onew.attachEvent("onclick", //For IE
function(){fnDisplay_Computers("'" + alines[i] + "'"); });
}
catch(e) {
onew.addEventListener("click", //For Mozilla-based browsers
function(){fnDisplay_Computers("'" + alines[i] + "'"); },
false);
}
I think #3 protesteth too much. In a lot of situations I'm building a table dynamically and need to pass parameters to the callback function. It isn't a typesafe issue since my variable parm is an integer index to the table row in question. Here's code with both a variable and fixed parameter that seems to be cross-browser compliant:
for (i = 0; i < arrTableData.length; i++) {
eleTR = objTable.insertRow(i + 1);
cell = eleTR.insertCell(0);
cell.width = "21";
var myElement = document.createElement('img');
myElement.setAttribute('src', 'images/button_down.gif');
myElement.setAttribute('alt', 'Move item down');
myElement.onclick = new Function('moveItem(' + i + ',0)');
cell.appendChild(myElement);
}