Why there are spaces between my <span> elements in contenteditable? - javascript

I'm trying to build a Notion-like editor in Svelte, I've made some good progress, but I'm now scratching my head trying to figure out why exactly i get spaces between span elements.
I'm dividing the text in spans based on their formatting, here's an example of an output:
<div contenteditable="true">
<p>
<span contenteditable="true" id="a1" data-index="0">some text</span> //normal text
<span contenteditable="true" id="a2" class="bold" data-index="0">some other text</span> //bold text
</p>
</div>
In between them a #textnode with a little bit of space gets created and I can't figure out how to avoid it. I've tried multiple CSS options, but no matter the CSS properties, these spaces persists. My understanding is that these #textnodes are on the DOM itself.
Here's the code responsible for this output. The $fragments array is modified through keydown events and binds to the HTML of the span element, synchronizing the changes. The default behaviour of a character insertion is prevented through e.preventDefault().
{#each $fragments[$selectedFragment].children[childIndex].text as fragment, i}
{#if fragment.tag == "span"}
<span
contenteditable="true"
id={fragment.id}
class="edit-row"
data-index={i}
class:bold={fragment?.formattingOptions?.bold}
class:italic={fragment?.formattingOptions?.italic}
>{fragment.text}</span>
{/if}
{/each}
I've also tried putting the code in a single line to avoid generating spaces somehow, but no changes:
<p id={$fragments[$selectedFragment].children[childIndex].id} class="{$fragments[$selectedFragment].children[childIndex].format} edit" data-index={childIndex} bind:this={element}>{#key $justKey}{#each $fragments[$selectedFragment].children[childIndex].text as fragment, i}{#if fragment.tag == "span"}<span contenteditable="true" id={fragment.id} class="edit-row" data-index={i} class:bold={fragment?.formattingOptions?.bold} class:italic={fragment?.formattingOptions?.italic}>{fragment.text}</span>{/if}{/each}{/key}</p>
Here's the demo: https://epub-theta.vercel.app/fragments
If you need any further details, please let me know.
Thank you for your help.
UPDATE: adding elements through browser's inspector does not generate these spaces, so it has something to do with how they get inserted by Svelte, or some space/line break somewhere somehow. Still searching.
UPDATE: definitely an issue with how Svelte renders the HTML, there is whitespace between elements by default and this is the outcome. No clear solution in sight, only some hacky workarounds that end up breaking other code.

either place the span tags next to each other on the same line or comment out the space.
<div contenteditable="true">
<p>
<span contenteditable="true" id="a1" data-index="0">some text</span><!--
--><span contenteditable="true" id="a2" class="bold" data-index="0">some other text</span>
</p>
</div>

This problem is related to the ongoing issue of whitespace handling in Svelte. There are solutions for SSR pages, but since in my case the HTML is user-generated I couldn't find a non-disruptive way to fix it.
For now I'll use this workaround which leaves the impression that these small spaces are not there:
span {
margin: -0.8px;
}

Related

Contenteditable elements change ID after modifications

I have some problems with a contenteditable div annoying behaviour. I have a few elements inside, let's say the code looks like that:
<div contenteditable="true">
<p id="element-id-1">element-id-1</p>
<p id="element-id-2">element-id-2</p>
</div>
All works as intended except for one thing - when I triple click the first paragraph to select and remove it (with delete or backspace) the second paragraph content 'jumps' into its place, but retains the first paragraph ID. Is there a way to prevent this, so after I triple click the first paragraph and remove it, the second paragraph remains with the same ID (#element-id-2)? JSFiddle with described functionality here: https://jsfiddle.net/t8e28bmx/ Thanks!
Try this code.
<div contenteditable="plaintext-only">
<p id="element-id-1">element-id-1</p>
<p id="element-id-2">element-id-2</p>
</div>
Reference: https://w3c.github.io/editing/contentEditable.html#h-contenteditable
<div>
<p contenteditable="true" id="element-id-1">element-id-1</p>
<p contenteditable="true" id="element-id-2">element-id-2</p>
</div>

Can't vertically align divs

I'm having a problem where I seem to be unable to get some divs to line up nicely. It doesn't really matter whether its in an accordion (like it is) or not. The accordion doesn't make a difference. Here's what it does now:
I think it's obvious that i'd like them to be vertically aligned together, at the top (as some are longer than others) If you could out, then that'd be great.
I tried to make a fiddle of it but failed miserably, so if you want to see it in action, you can find it at http://thephotoshopwirral.com/covers.php
If you need any more info, just ask. I'll do my best to help you.
You have <br> tags between your labels. Remove them.
<label><br><label>
You're not closing the label elements. I suggest validating your HTML.
Also you can't put div elements within label elements.. I suggest restructuring.
Instead of closing <label> tags, you're opening new ones:
<label><div class="phoneitem">
<img src="http://www.thephotoshopwirral.com/image/phones/bold.png" width="200" height="250" alt=""/>
<input type="radio" name="phones" value="1" id="phones_">
Blackberry Bold
</div><label> <!-- THIS LINE -->
Replace <label> in the specified line to </label>.

Can't reach element by using a combination of .parent() and .next() methods

I'm binding the following function to a toggle button I have created to hide/unhide content that is located bellow the button, but on an other level in the DOM.
var togglelabel = packagehead.append("<div>").children().last().addClass("togglewrap").append("<label>")
.children().last().addClass("toggle android header-toggle")
.on('click', function(){
$(this).parent().parent().next('.hidable').toggle();
});
The html structure looks like this:
<div>
<div class="packageheader">
<span>Package #1501</span>
<div class="togglewrap">
<label class="toggle android header-toggle">
<input type="checkbox">
<p>
<span>More</span>
<span>Less</span>
</p>
<a class="slide-button">
</a>
</label>
</div>
</div>
<br>
<p class="hidable">
<pre>content here</pre>
</p>
</div>
This code doesn't work to hide the <p> with the class .hidable.
I've tried 'debugging' the code using console.log() to see what element 'this' represents and found that it does, as expected, represent the label element.
So I thought that using the following chain:
$(this).parent().parent().next('.hidable').toggle();
Would correctly go 2 levels up to the <div class="packageheader"> and then take the next sibling with the class hidable, which would be <p class="hidable">
Here is a screenshot of the structure, to be sure I didn't miss anything:
You can do this :
$(this).closest('.packageheader').nextAll('.hidable').first().toggle();
Note that it's slightly preferable to use closest instead of parent().parent() as it won't break as easily when the HTML changes and it's easier for the maintainer to decipher what the code does.
Note also that your HTML is invalid, you can't have a PRE inside a P.
Demonstration

Html rendering closing tags automatically

I am working on a Native mobile application CMS system using client-side technology(javascript via JQuery for the most part) and have implmented a templating system similar to that of ASP.NET.
So suppose I have a navigation control which has a starttemplate, endtemplate and itemtemplate as follows:
<div data-type="navigation" id="navigationControlDemo">
<div data-type="starttemplate">
<ul id="Menu">
</div>
<div data-type="itemtemplate">
<li>[[Text]]</li>
</div>
<div data-type="endtemplate" >
</ul>
</div>
</div>
My problem is the browser as it laods seems to decide to want to fix the html so that the tags are closed "properly" as follows:
<div data-type="navigation" id="navigationControlDemo">
<div data-type="starttemplate">
<ul id="Menu">
</ul>
</div>
<div data-type="itemtemplate">
<li>[[Text]]</li>
</div>
<div data-type="endtemplate" >
</div>
</div>
I've tried some workaround and quick hacks to no avail..
I have tried replacing the "<" ">" with certain characters and then replacing them after load but the problem still occurs.
Your code is not valid html. You can't interleave tags (open it in one place and close it in a completely different place). Tags have to be properly nested.
Valid:
<p>
<ul>
</ul>
</p>
Invalid:
<p>
<ul>
</p>
</ul>
Note that you can't properly indent the second one without it looking "off". Please make sure your html is correct first, then the browser will behave properly.
-update- since you want client-side templating, what you don't want is the (invalid) html to be parsed as html. However, it's obviously also not valid xml, which is what you might want.
What you could do is wrap the content of the secions as CDATA.
<div data-type="navigation" id="navigationControlDemo">
<div data-type="starttemplate"><![CDATA[
<ul id="Menu">
]]></div>
<div data-type="itemtemplate"><![CDATA[
<li>[[Text]]</li>
]]></div>
<div data-type="endtemplate" ><![CDATA[
</ul>
]]></div>
</div>
Now if you would interpret that as xml, the parts within the templates are considered text rather than markup.
Basically, you're asking for a way to force a browser to display malformed, messy tag-soup. Thank God most browsers don't just blindly render that kind of code, because the results would be unbearable and make your page unintelligible all together.
That said, if you view the raw page source, you'll probably see the un-corrected, raw markup. But because the browser can't really render it, the engine steps up to the plate and does its best.
But please, don't waste your time finding a way around this, it's just a feature to help you, not to annoy you. Also: HTML5 has a lot more rules about what is allowed and what is not, and how the browser is expected to deal with invalid markup (in your code: <div><ul></div> is deemed invalid, for example). If you feel up to it, you can look at the W3C specifications for more info on the subject.
You can't have <div> elements as children of a <ul> element.
<ul>s only accept <li> tags as children. Specification
Also, it appears you're nesting the tags incorrectly, here:
<div data-type="starttemplate">
<ul id="Menu">
</div>
You can't close that <div> if the <ul> is still open. That's why the <ul> is prematurely closed.
Instead of having tags such as I used keywords to represent the < and > and then render all the correct html using the templating system and replace those keywords back to , and > and add it to the document afterwards. Annoying syntax in my template tags, but it works the way I need it to.

Clicking to an anchor from and image map causing page to move unnecessarily

I realize I'm still pretty new here, but I have a static image (Google Map but not using Google API yet) in which I have created hotspots which will pull up location data that lives in a table to the left of the map. When a hotspot is clicked on, however, the page scrolls down so that the top of the map (the image map) is at the top of the screen even though the information is directly beside the map.
I am assuming this is because the anchor is seeking to load at the top of the screen. This would be okay except that my header is now pushed out of the screen. Is there a way for the page to not "move" when the hotspot is clicked?
The page can be seen here: http://www.mydillonsupply.com/default.aspx?page=customer&file=customer/disupp/customerpages/locations_page.htm
Instead of using the default browser behavior (for anchor tags) just block it, and scroll the box yourself. I can see you are already using jQuery. So something like this ought to do the trick.
$('area').bind('click', function(e) {
e.preventDefault();
// the div in question has nothing uniquely identifiable as it is now,
// assign it a unqie class or id so you can select it
var findAnchor=this.href.split('#')[1];
$('#the_div').scrollTop($('a[name="' + findAnchor+'"]').next().position().top);
});
It's kinda hard to test in the context of that page, but if you set up a fiddle with just that part of it I am sure this could be made to work right pretty easily.
(edit) - OP set up a fiddle with the problem, updated version here:
http://jsfiddle.net/H3Mz6/9/
The code above has been updated to reflect what actually works. I also added the id "the_div" to the div surrounding the table of locations. Here's how it works:
1) get the part of the href after the # - the browser may add the full url.
2) find it, then get the next() element, because the invisible anchor tags will report that they have no location information
3) then get the position().top value which is the postion of that element relative to it's container
4) then scrollTop(..) to it
Contrary to #colinross's suggestion, there's nothing that's either non-extensible nor inflexible about imagemaps. Quite the opposite, they are the only way you can have irregularly shaped hotspots without going to a heck of a lot of trouble, and that gives you a lot of power. All you need to do to make them do whatever you want is bind your own mouseover and/or click events to the areas, and call e.preventDefault(). It's all yours from there.
Yes, I like image maps, and I also wrote a plugin that does a heck of a lot with them. So I am fairly biased. But I am surprised by the trouble people go to in order to avoid them (like absolutely positioning anchor links, complex css, and so on) when they're dead simple, easy to use, work with every browser under the sun, and are far more powerful than positioning all your hotspots by hand. (And without an imagemap, or some crazy logic to figure out where the mouse is on your own, you're limited to rectangular areas anyway!).
The jump is happening because you are using an image map that is processing a click to the location #DillonLocationsMap.
It is the same result as having an in-page anchor like <a name="over_here" /> and a link elsewhere of Go over here.
I would suggest you don't use an image map to be honest and they are not very extensible nor configurable.
give the fish answer
Move the actual <map> element up, to for instance before the table#MainTable element. It will still technically jump, but your header should still be in view.
p.s. Tables for page-layout makes pandas cry ;(
When you click on the link, your <a name="Nashville"></a> tag relating to said city end up scrolling to the top of your <!-- table containing locations -->.
Subsequently, this will work the exact same way as with a "Top" link where you place an <a name="TOP"></a> at the top of your page and then a Back to top at the bottom of your page. It will try to put the <a name="Nashville"> as close to the top of the viewport as possible (example: http://mix26.com/demo/local_scroll/index.html).
You could try something like this (found here):
<html>
<head>
<title>Document Title</title>
<script type="text/javascript" language="javaScript">
<!--
function go_anchor(n){
document.getElementById("div1").scrollTop = document.getElementById(n).offsetTop
}
// -->
</script>
</head>
<body>
To anchor 1<br />
To anchor 2<br />
To anchor 3<br />
To anchor 4<br />
<div id="div1" style="position:absolute; left:30; top:100; width:330; height:200; clip:rect(0,330,200,0); overflow:auto; padding:5;border:2px solid black">
<p>To anchor 1</p>
<p>Dummy Text 2</p>
<p>Dummy Text 3</p>
<p>Dummy Text 4</p>
<p>Dummy Text 5</p>
<p>Dummy Text 6</p>
<p>Dummy Text 7</p>
<p><span id="sp1">Anchor 1</span></p>
<p>Dummy Text 9</p>
<p>Dummy Text 10</p>
<p>Dummy Text 11</p>
<p>Dummy Text 12</p>
<br/><br/><br/><br/><br/>
<span id="sp2">Anchor 2</span>
<br/><br/><br/><br/><br/>
<span id="sp3">Anchor 3</span>
<br/><br/><br/><br/><br/>
<span id="sp4">Anchor 4</span>
<br/><br/><br/><br/><br/>
<br/><br/><br/><br/><br/>
The End
</div>
</body>
</html>

Categories