Update: 27th December 2016
I did change the heading, since every DOM element could be the target (it actually doesn't matter if it is a <p> element or not).
I've provided some more informations about what I'm using and what I'm trying to achieve. Maybe there are native electron ways to achieve this? Or libs which could help me too?
Product: I'm going to extract tgz files with XMLs in it. Those XMLs will be used to automatically fill tables in the finished product. After that the tables and paragraphs will be editable where users can add new rows to the tables and also add new paragraphs to the page.
Framework: I'm using electron to fire the whole thing up.
Backend: NodeJS 7.x.x to make use of ES6 features
Libraries: jQuery, Bootstrap, Angular, Materialize, lodash, async, moment
Please keep in mind that I already did achieve all of my product needs. My original question was and still is if there is a more performant way of doing this:
I have a html page which can have 'n' containers called pages. A page can hold multiple <p> elements. This <p> elements are set to contenteditable="true".
Now I'm trying to create a javascript function which is checking the single page height with something like this:
// Set max container height to 10cm.
let containerMaxHeight = 377.95276 // 1 cm = 37.795276px;
if(containerElement.clientHeight > containerMaxHeight){
/**
* do desired stuff.
*/
}
everything easy so far. The function is getting the innerHTML of the <p> element which is currently beeing edited and "break the site" into a new site if the page height is above the limit. I have thought out a recursion wich is removing words (most of the time 1-3) of the old <p> element and inserting them to a newly created page with a <p> element until the maximum height of the old page is set to its maximum value.
Here is an example of my recursion (simplified) which is removing words from the end of innerHTML like this:
let lastWordToBeRemoved = oldParagraphElement.split("\\s+").pop();
// append old value to new <p>
newParagraphElement.innerHTML += lastWordToBeRemoved;
// remove last Word from old <p>
oldParagraphElement.innerHTML.slice(0, -lastWordToBeRemoved.length);
/**
* Recheck height of old page container if it is above the
* maximum redo above code
*/
I've startet out with this example:
https://delight-im.github.io/HTML-Sheets-of-Paper/
as you can see the pages are getting bigger and bigger if you edit them. I've already prevented that with my JS function.
Now that you have an idea of what I'm doing: Is there a more performant and or elegant way of doing this? I'm highly interested to hear how you would solve this problem.
If there is anything still unclear let me know, I will update my answer.
Thank you in advance!
Regards,
Megajin
Instead of splitting words, I think you should insert another p element into the expected position instead. Then you can easily move the exceeding paragraph into the new page. For example
paragraphElement.innerHTML = paragraphElement.innerHTML.replace(lastWordToBeRemoved, '</p><p class="exceeding-paragraph">' + lastWordToBeRemoved);
newPage.insertBefore(oldPage.querySelector('.exceeding-paragraph'), newPage.firstElementChild);
How does one insert, or better, replace the current selection with some content and then select it?
Here's my text: Hello nice world!
As you can see nice is selected by the user. Now he clicks a button and this code is run:
editor.execCommand('mceReplaceContent', 'nasty');
This works just fine, the result is: Hello nasty world, but nothing is selected.
How do I make it automatically select nasty in the result content?
This seems like a very natural thing for one to want to do, but can't seem to find a straight-forward solution. I need this to work in mostly two cases 1) I am wrapping the selected text in a f.e. span element or 2) I am removing the wrapping span element.
I know there are better ways of dealing with nodes, but I'm more concerned about the pure text scenario right now.
Thanks in advance!
P.S. I am using TinyMCE 3 not 4.
I found this in the docs (API 3.x)
// Sets some contents to the current selection in the editor
tinyMCE.activeEditor.selection.setContent('Some contents');
// Selects the first paragraph found
tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
The setContent function does practically the same as execCommand('mceReplaceContent'). I did not found something like the easy DOM properties selectionStart & selectionEnd.
My main mission: Is to get the text of the next and the previous objects to the chosen object - To display the image (and its titles) Previous & Next.
Before that I have a problem: to get text of a selected object, from an index to a variable.
The problem: Every time I pick a random object, the variable does not change but the text is added to the existing text in the index.
I made a DEMO, would appreciate your help.
$(document).ready(function hintProject(){
$('#nextProject, #prevProject').click(function(){
subtitle = null;
subtitle = $('#client-sub.active').justtext();
$('#next_target_title').text(subtitle);
alert (' text::: ' + subtitle );
});
});
It looks like jQuery simply can't find the objects you're specifying. I don't think the problem is with the snippet in the question. I think the problem is with your document ready function.
To debug, try simplifying your problem by cutting out all of the additional complexity of the setup script and just set up an HTML page that is in the state you want. It's much easier to understand 1 problem than 2 or more.
Also, try simplifying how you're specifying an active item: a single class on the portfolio item would make your life easier. Then you can specify css and such based on the parent instead of adding multiple classes to multiple things inside the each portfolio item.
Initially I asked this Question and written my own plugin to achieve the same,But i am facing very strange issue regarding to css of table.
After applying the plugin table cells borders are getting dis-sorted.
jsFiddle of the problem: Problem demo
In fiddle you can see that after first cell of the first tr, the header border line and table border line don't line up. I want the border line of thead cells and td cells to line up.
Can anyone tell me how to achieve that?
Lets start by doing a bit of a clean up of the code you posted so I can actually read your code and maintain a firm grasp on reality while we go down this rabbit hole.
If you write clean code, your problems will be exceptionally easier to see.
So lets clean it up and watch as all the problems here reveal themselves.
Step one: Your jsFiddle sets it to run "onDomReady," which basically means you've got $(document).ready(...) calling all the code in the box, which is fine, except you're got another got $(document).ready(...) inside there. Lets change that.
Step two: Lets add some white space and proper indentation in there, and stop using these one letter variable names.
{} are scope brackets, they should indent, not cover everything, they let us know what part of scope something is in.
Don't write .each() loops on one line, this adds no value and makes your code confusing to read.
$t should be called something meaningful, lets try element, because it holds the $(this) element, which is the active element you're working with.
w should be called something meaningful, but since you only use it twice I'm just going with element.width().
o needs to be less ambiguious, lets go with obj.
Step three: Selection structures
if(typeof(i)=='number')o.height=i;
else if(typeof(i)=='object')o=i;
else if(typeof(i)=='undefined')o={height:300}
Break that up, make it readable. Saving lines doesn't make you a better program, writing clean and easily understandable code will.
Why not use the switch-case statement?
switch (typeof(i)){
case "number":
o.height=i;
break;
case "object":
o=i;
break;
case "undefined":
o={height:300};
break;
}
Step four: Don't in-line styles. Just don't. There's no reason to do it, and it makes everyone's life harder.
Instead, lets just place the styles gently into the style sheet where it belongs, and make the parent=$('...') line look like parent=$('<div><div></div></div>').appendTo('body');.
Step five: Closure doesn't pass any value to arguments
After a bit of clean up, we see this block of code:
self.width(self.width() -
function(width){
var parent,child;
if(width===undefined){
parent=$('<div><div></div></div>').appendTo('body');
child=parent.children();
width= (child.innerWidth()) - (child.height(99).innerWidth());
parent.remove();
}
return width;
}()
);
Okay, that's a problem. Lets cut out a few lines to point out the problem here:
self.width(
self.width() -
function(width){
/*...*/
if(width===undefined){
/*...*/
}
return width;
}()
);
So, a quick refresher on this pattern you have here:
(function(arg1){
/*code*/
})(data);
Data gets passed to arg1. Arg1 declaires a variable in the scope local to that function, it doesn't get anything from outside. Outside data is passed in through the set of () that call the function, which your code had left abandoned. Think of it this way:
var msg = function(text) {
alert(text);
};
Then you call it as...
msg("hello world");
What your closure is doing is almost the same thing, except where you define your function, you also call it. Thus...
(function(text) {
alert(text);
})("hello world");
So, you need to pass a value of some sort into there, other wise this whole thing is always undefined. Lets do that. What are we passing? I have no way to be sure. This is why programmers need to add comments to their code.
Step six: Comment your code so people other than yourself will look at this code and have not a damn clue what you truthfuly wanted to do, and can only guess. It's like you posted a 200 point bounty and didn't bother helping people who want to help you. Why are you doing this to yourself, dude? Why couldn't you just go //This is what this does to give me a hint? What did I ever do to you?
Step Seven: Lets see if we can make the JS changes work with the JS Fiddle
Great odin... that HTML's 2000 lines long?
Okay, I'm working with pastebin here for the sake of saving space in the post here.
Alright, you started off with this: http://pastebin.com/xjmm4cev
You're using a lot of no-wrap, and putting classes onto individual elements. You shouldn't have to do this on each HTML element, CSS takes care of that very effectively, so lets go ahead and just rip out all the nowrap=nowrap and class="header" stuff (we'll put it back in a moment, but only ONE per group, not each element).
Then lets get rid of the useless blank lines.
Lets run this through HTML tidy and get it nice and indented correctly.
http://pastebin.com/uHtSZ4h5
Much easier to read over. Okay, so what do we see here? Well, it looks like you keep going in circles, cutting and pasting the same thing again and again. You also in-line javascript such as using onchange and onclick attributes. This is generally an awful thing to do to your code, and makes it hard to maintain (as I'm sure you've seen with this 2000+ line beast of cut+paste 27 times in a row).
So, lets take a look here:
elements, not inside a form
Elements in a table that are outside of rows, but not head/body/footer sections of a table
Code that's a huge pain to maintain because it keep going in circles, if you need to change this, you're basically screwed.
Lets fix all that.
We're going to use events in the tags, rather than in-line things. So, all of those in-line onchange and click attributes get the boot.
All of these inputs that are just floating around need to get put into a form, and taken out of this place in tables that only rows or table sections belong in.
Figure out how we can not have excessive input elements, if we can help it.
What the heck is the )="" that you have on every input button? Deleted.
So, here's all of your hidden boxes: http://pastebin.com/LXZSkvyf which I've removed, because we don't have a anywhere.
And here's what the code looks like without all of these weird things in it: http://pastebin.com/MiaJTGpb
Much more readable, but still not quite there.
Step 8: What can you do to make the HTML of the table work better?
You're using Thead and Tbody, and that's good.
You're using attributes for things like cellpadding, that's bad.
You've given each body row an ID. I don't feel you needed to do this, but it's not always bad. However, I'll show you how you could work without it.
You give some selects a select-box class, but it's nowhere in the css. I've removed it.
You give a title attribute to the selects that says "option_value". The title attribute is generally used to make a tool-tip popup when you leave the mouse over something. I'm not sure what you're going to do here, but that's bound to confuse your user. I'd highly suggest giving something better than option_value in that place.
You keep using the ID Submit_FMS_AddDelivery. HTML ID tags are meant to be unique, and used only once on one element. You've got it 27 times, that's bad. I don't think you need an ID on it, so I've removed it.
You've also got input-btn going on, I'm removing it, because you haven't shown it's used anywhere.
You probably don't need any of these ID tags on select and TR to be honest, so I'm pulling them.
What's that look like? Basically, you've got what's almost just the data, in it's nice pure form. That's good. http://pastebin.com/UNS6CAtb
Step 9: What were you trying to do?
Lets step back and take a look here.
All you really wanted to do was keep a fixed header, but you've ended up doing a lot of JavaScript hacks and manipulating the DOM in a lot of places. We need to stop doing this. Is there a simpler way to do what you want?
I'll have to expand on how to make this function with the rest of your stuff, but I get up for work in a few hours. I'll update again with more... But we'll get there.
Right now, because I haven't fixed what I took out, it doesn't look right. But I'm tired.
So, here's where we are so far: http://jsfiddle.net/5C6z7/
Plus those inputs we took out (and will be going back in later, in a different way)
Looks like all you have to do is to take the padding into account: each cell has 3 px padding both left and righ so you have to add 6 px to the width:
$t.find('tr:first th').each(function(){cols.push($(this).width()+6);});
Otherwise the cells with only one word inside will "push" the actual width a bit wider so that the word will fit and other cells with space to move will compensate by becoming a bit narrower. The header and the body both do this independently with different contents which creates the difference in actual cell widths.
EDIT: For Firefox you also need to widen the table so that the cells fit. After calculating the column widths add
var actualWidth = $t.width()+cols.length*6;
$t.width( actualWidth );
And later change the wrapper to:
$wrap.css({width:actualWidth,height:o.height,overflow:'auto'});
EDIT 2: To have both the header and the body scroll simultaneously you need to wrap them both to an outer div that handles the scrolling.
var $outerWrap = $( '<div>' ).css( {width:"300px", overflow:'auto' } );
var $wrap=$('<div>').css(
{ width:actualWidth,height:o.height,"overflow-y":'auto', "overflow-x":'hidden' }
);
$firstRow.wrap( $outerWrap );
$firstRow.after( $wrap );
$wrap.append( $t );
Demo: http://jsfiddle.net/YcRTz/2/
What about replacing '<th>' tags with '<td>' tags?
No extra js code required.
http://jsfiddle.net/spQAh/7/
Okay so instead of troubleshooting or using anything complex, I came up with simple CSS + JS solution to your problem. Take a look at http://jsfiddle.net/TdLQT/
Obviously, the process of making header static, can be made dynamic, meaning trigger it after user scrolls a bit or have it there by default or count other object's position from top window and trigger static behavior when it reaches or exceeds certain pixels. I can provide that if you give me details on exact design of your HTML page.
Anyways, I as you can see, have used fixed pixel heights which you can choose not to or make them elastic or dynamic. What is important to know is that, though solution relies on JS a bit, output position is purely from CSS. I am using classes to manipulate the position.
the simple solution is that when you calculate the width of the column you do not include padding. So you need to change the line
$t.find('tr:first th').each( function() {
cols.push($(this).width());
});
to
$t.find('tr:first th').each( function() {
cols.push($(this).outerWidth());
});
if your cells have margins use .outerWidth(true)
This works in quirks mode with IE7&8 and Chrome IE8 has a problem in strict mode. However I believe that that problem is caused by the added scrollbar. If you want to use IE8 in strict mode you have to allow for the width of the vertical scrollbar..
im having a firefox issue where i dont see the wood for the trees
using ajax i get html source from a php script
this html code contains a tag and within the tbody some more tr/td's
now i want to append this tbody plaincode to an existing table. but there is one more condition: the table is part of a form and thus contains checkboxe's and drop down's. if i would use table.innerHTML += content; firefox reloads the table and reset's all elements within it which isnt very userfriendly as id like to have
what i have is this
// content equals transport.responseText from ajax request
function appendToTable(content){
var wrapper = document.createElement('table');
wrapper.innerHTML = content;
wrapper.setAttribute('id', 'wrappid');
wrapper.style.display = 'none';
document.body.appendChild(wrapper);
// get the parsed element - well it should be
wrapper = document.getElementById('wrappid');
// the destination table
table = document.getElementById('tableid');
// firebug prints a table element - seems right
console.log(wrapper);
// firebug prints the content ive inserted - seems right
console.log(wrapper.innerHTML);
var i = 0;
// childNodes is iterated 2 times, both are textnode's
// the second one seems to be a simple '\n'
for(i=0;i<wrapper.childNodes.length;i++){
// firebug prints 'undefined' - wth!??
console.log(wrapper.childNodes[i].innerHTML);
// firebug prints a textnode element - <TextNode textContent=" ">
console.log(wrapper.childNodes[i]);
table.appendChild(wrapper.childNodes[i]);
}
// WEIRD: firebug has no problems showing the 'wrappid' table and its contents in the html view - which seems there are the elements i want and not textelements
}
either this is so trivial that i dont see the problem OR
its a corner case and i hope someone here has that much of expirience to give an advice on this - anyone can imagine why i get textnodes and not the finally parsed dom elements i expect?
btw: btw i cant give a full example cause i cant write a smaller non working piece of code
its one of those bugs that occure in the wild and not in my testset
thx all
You are probably running into a Firefox quirk of following the W3C spec. In the spec the whitespace between tags are "text" nodes instead of elements. These TextNodes are returned in childNodes. This other answer describes a workaround. Also Using something like JQuery makes this much easier.
I would expect this behavior in any browser as the += operand overwrites what is already in the table by definition. Two solutions:
Instead of receiving HTML code from your PHP file, have the PHP generate a list of items to add to the table. Comma/tab separated, whatever. Then use Table.addRow(), Row.addCell() and cell.innerHTML to add the items to the table. This is the way I would suggest doing it, no point in creating GUI elements in two separate files.
The other solution is to save all the form data that's already been entered to local JavaScript variables, append the table, and then reload the data into the form fields.
Well, returning a JSON object with the new data seems like the best option. Then, you can synthesize the extra table elements by using it.
In case one is forced to get plain HTML as response, it is possible to use var foo = document.createElement('div');, for example, and then do foo.innerHTML = responseText;. This creates an element that is not appended to anything, yet hosts the parsed HTML response.
Then, you can drill down the foo element, get the elements that you need and append them to the table in a DOM-friendly fashion.
Edit:
Well, I think I see your point now.
wrapper is a table element itself. The nodes reside under the tbody, a child of wrapper which is its lastChild (or you can access it via its tBodies[0] member, in Firefox).
Then, using the tBody element, I think that you would be able to get what you want.
BTW, You do not need to append the wrapper to the document before appending its children to the table, so no need to hide it etc.