I created a contentEditable div with a text portion and a link. Double clicking the link will select the link text.
<div contentEditable="true">
This is a text and This_is_a_link
</div>
Afterwards calling document.getSelection().getRangeAt(0).startContainer will return the div:
// => <div contenteditable="true">
Instead of the link. I cannot find a way to find which part of the div is selected.
See this jsfiddle (double click the "This_is_a_link" and there will be a console log with startContainer):
http://jsfiddle.net/UExsS/1/
(Obligatory JS code from the fiddle)
$(function(){
$('a').dblclick(function(e) {
setTimeout(function() {
console.log(window.getSelection().getRangeAt(0));
}, 500);
});
});
Note, that Chrome has the correct behavior, and running the above jsfiddle in Chrome will give textElement for startContainer.
Has anyone run into this issue? did you find a workaround?
Don't think its a bug of Firefox, just a different kind of implementation. When you double click the link, Firefox selects not only the text, but the whole a-tag, so the parent node of the selection is correctly set to the div container.
I added these few lines of code to your fiddle to proof that point:
var linknode = window.getSelection().getRangeAt(0).commonAncestorContainer.childNodes[1];
console.log(linknode);
console.log(window.getSelection().containsNode(linknode, false));
Forked fiddle: http://jsfiddle.net/XZ6vc/
When you run it, you'll see in the javascript console that linknode contains your link, and the check if the link is fully contained in the selection returns true.
This is also one possible solution to the problem, albeit not ideal one. Iterate over all the links in your contenteditable and check if one of them is fully contained in the selection.
Though one word of advice: Don't reinvent the wheel if you don't have to ;-) There's quite possibly some libraries / frameworks out there that fit your needs.
Related
I am using Materialize.css on my portfolio site, and am using the npm package clipboard.js. I am using this within a floating action button, and the copy to clipboard functionality is working as intended (when user clicks the button, it copies my email to their clipboard like it should).
However, I want the tooltip to update from ""Click to copy my email to your clipboard!" to a success message like "Copied to your clipboard ✅". I have tried the code below and it won't actually update the page, though I can sometimes see the new message (it's just very inconsistent, which I don't want).
This is my html element:
<li>
<a id="email" data-clipboard-text="example#gmail.com" class="btn-floating red waves-effect waves-light tooltipped" data-position="left" data-tooltip="Click to copy my email to your clipboard!"><i class="material-icons">mail</i></a>
</li>
and here's my javascript:
var clipboard = new ClipboardJS('#email');
clipboard.on('success', function(e) {
var anchorElement = $('#email');
anchorElement.attr('data-tooltip', 'Copied to your clipboard ✅');
anchorElement.addClass('success');
anchorElement.tooltip();
// Reset after a timeout
anchorElement.mouseleave(function() {
setTimeout( function(){
anchorElement.attr('data-tooltip', 'Click to copy my email address to your clipboard!');
anchorElement.removeClass('success');
anchorElement.tooltip();
}, 300);
});
e.clearSelection();
});
I would like for the tooltip to show the updated value consistently, but I haven't been able to figure out what's wrong with the code I have. I can tell that it does update the html element, as I can sometimes see the updated text, but it's very inconsistent and for this feature to be worth using at all I need it to be very consistent.
Any help would be greatly appreciated!
The problem you're running into is that materialize.css adds in other DOM elements that eventually appear on screen as the tooltips themselves - elements that you aren't targeting right now - elements that you didn't write in your HTML. You're targeting the original data-attributes that materialize.css uses to create those other elements. Updating those data-attributes after render is too little too late... by then, the materialize.css library has already looked at those attributes and gotten what it needs.
If you look at the official docs page on tooltips, we can investigate a little on that page. Open your dev console and scroll towards the bottom you'll see four DOM elements with the class material-tooltip - these were added by the library, and these are the DOM elements that actually get shown on screen.
Open those divs up and watch what happens to them when you mouse over the Bottom, Top, Left, Right buttons on the screen. As each tooltip appears, you should notice that the message-to-be-displayed gets injected as text into that element, and it animates some CSS properties.
If you want to change the text being displayed, you can probably skip editing the data-attributes (though if the library occasionally refreshes the content, changing the attributes might still be a good idea)... instead, we need to edit the text being shown in ^^THESE^^ elements.
If you only have one tooltip being displayed on that page, it should be as simple as something like this:
$('.material-tooltip').innerText = 'Copied to your clipboard ✅'
or if you have multiple on that page, you can attempt to identify which div is for which tooltip, but I suspect that could end up being unreliable. It would probably be safer to update ALL of the divs... the library will overwrite the content anyways next time it renders... which again, it gets from your data-attributes. Updating all of them would look something like:
$('.material-tooltip').each( eachDiv => {
eachDiv.innerText = 'Copied to your clipboard ✅'
})
here's only solution I found that works for me .. disclaimer - it's a bit hacky, basically it destroys tooltip on click and rebuilds it with new attr, then after a timeOut resets it back.
$('#shareLink').on('click', function (e) {
$('#shareLink').tooltip('dispose').removeClass('material-tooltip').attr('title', 'Link copied!');
$('#shareLink').addClass('material-tooltip').tooltip('show');
setTimeout( function () {
$('#shareLink').tooltip('dispose').removeClass('material-tooltip').attr('title', 'Copy link to Clipboard');
$('#shareLink').addClass('material-tooltip').tooltip('enable');
}, 2000);
});
On the surface this should be easy:
CKEDITOR.instances[Object.keys(CKEDITOR.instances)[0]].insertHtml( html );
...where html is a string of an actual HTML tag. Sadly, however, this doesn't work. When I click the button on my page that calls this code, nothing happens. It doesn't appear anywhere in the document at all, not even in Source mode.
I tried using insertElement:
var element = CKEDITOR.dom.element.createFromHtml( html );
CKEDITOR.instances.editor1.insertElement( element );
...and all it did was stick a little red flag in the document that was nothing; if I saved the document and reloaded it, it was gone.
The goal is to insert:
<a name="something"></a>
But the only thing that works is insertText() and that turns it into "safe" text, i.e. the < and > turn into lt; and gt;.
Help please? :)
I guess you used the code from the CKEDITOR Documentation (https://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement)
You probably ran into an issue, which says, that empty anchors show
a little red flag in the editor
(https://dev.ckeditor.com/ticket/14689). Unfortunately there seems to
be no way of CKEDITOR from doing this.
Empty Links are removed from
CKEDITOR automatically. You can add data-cke-survive="true" so these
links aren't removed,
Regards
I am trying to create a textarea where I can add HTML elements which should be draggable on character level. This question is related to another I am currently asking but that is GWT related. In order to create such a text area I am trying to understand how to do it step by step. This is what I got so far:
JSFiddle
JavaScript:
function smartdrag(e) {
var id = e.target.getAttribute('id');
if (id!=='plzdragme' && id !== 'plzdragmeimg') {
e.stopPropagation();
e.preventDefault();
return;
}
}
HTML:
<div contenteditable="true" ondragstart="smartdrag(event)">
More blah
<img src="http://lorempizza.com/80/80/2" id="plzdragmeimg" draggable="true" class="fancy-img"/>
about
<span id="plzdragme" contenteditable="false" class="fancy" draggable="true">DRAGME</span>
Sparta!
</div>
But for some reason I can only drag the img. What I actually need is to be able to drag a span or even better a div..
How can I accomplish that?
PS: Please no jQuery if possible.
I googled around a bit to see how HTML5 dragging works, and I found that in order to get a DOM element to be draggable, you have to add the new 'draggable' attribute to it, and set it to true. The confusing part about your problem is that images and links are draggable by default!
I wasn't really able to test this well because your script throws an error saying your function smartdrag does not exist.
Though I'm pretty hopeful about this possible solution as it covers your problem entirely, and comes from a trustworthy documentation.
I need to change the text that appears in the status bar of Chrome. Since it's not possible to use status.text anymore (for security reasons) I am wrapping an <a> tag over <body> so my code is <body>...</body> .
It's working as expected cause every time the user moves the mouse inside the page the status bar shows exactly what is inside the href attr of <a>. The problem: I did not realize till now that I cannot select any text inside the page cause if it's treated as link. I am using return false onclick event of and it works great cause the user is never redirected however it's not possile to select text inside the <a>.
Is there a CSS property that allows me to change that behaviour? It only occurs if <a> tag.
For the sake of hacking. This is invalid markup and bad code, but as a proof of concept (at least for Chrome).
One could use various combinations of mouse events, range selection and editable. Tricky part is to calculate what and where to select.
Sample code should give you selection of the first words in a paragraph; as in: click on the start of each paragraph like somewhere in "Lorem ipsum" or "Duis posuere" to select some of the words. This could then be combined with mousedown, mousemove, mosueup etc. to select correct text.
Chrome only Fiddle
You can try the CSS property pointer-events.
a{pointer-events:none} would disable the mouse event for that element.
But a better approach would be to add the URL in data-attribute and on click event you can navigate to those URL with location.href.
Will that help?
Ok, just for fun, and this is not the real hack but it something close:
Do like this:
<body>
<div class=container>bla bla bla</div>
<a href="my custom text" id=the_hack></a>
</body>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
<script>
$(".container").hover(function(e){
$("#the_hack").trigger("focus");
});
</script>
Of course it will have some limitations, when you select text the status bar will disappear, also if you highlight text move the mouse out of the div than back in, the selection will be lost, but hey it's a hack.
See here what I mean.
The problem I found is the following:
Situation: I have overall div that has a inline-block display. Inside it are two element that have an inline-block display as well.
Then I add (thanks to JavaScript) a <br/> between the two elements. The second goes to the next line, which is the normal behavior.
Buggy part: The <br/> is then removed (JavaScript again) and... the display doesn't change. It appears that the box of the overall div is not recalculated. In the end I have two similar markup that doesn't appear the same way (which is a bit problematic, isn't it).
It works fine on Firefox (it appears to be webkit based as the Android browser behave the same way). So my question is, is there a workaround that doesn't use methods that will alter the DOM? The library used is jQuery.
A test case here.
EDIT: As suggested by duri, I filled a bug report in webkit bugzilla, it's here. But I'm still looking for a workaround ;)
Way what I found: remove all childs from overall DIV, and then append all except BR's:
function removeBr(){
var ahah=document.getElementById("ahah");
var childs=[],child;
while(child=ahah.firstChild) {
if(!child.tagName||child.tagName.toLowerCase()!=='br')
childs.push(child);
ahah.removeChild(child);
}
for(var i=0;i<childs.length;i++)
ahah.appendChild(childs[i]);
}
http://jsfiddle.net/4yj7U/4/
Other variant:
function removeBr(){
var node=$("#ahah")[0];
node.style.display='inline';
$("#ahah").children("br").remove();
setTimeout(function(){node.style.display='';},0);
}
As a work around, you could set the style to display: block when you want them on individual lines and revert to inline-block when you want them to be friends.
I have created a JS Fiddle example
Which demonstrates this fix:
function addBr(){
$('span').css({ display: 'block'});
}
function removeBr(){
$('span').css({ display: 'inline-block'});
}
$("#add").click(addBr);
$("#remove").click(removeBr);
This bug still exists, so here's another workaround: http://jsfiddle.net/4yj7U/19/
$("span").css('display', 'none');
setTimeout(function(){
$('span').css('display', 'inline-block');
}, 0);
This makes Chrome re-render the spans and displays them properly.