Say I want to hide a span when the user highlights a bit of text containing that span, with the intention of copying that text on to his clipboard.
For example:
<p>The dragon <span class="tooltip">a large, mythical beast</span> belched
fire at St. George.</p>
I have found that in Firefox Mac, span.tooltip will disappear from view (in accordance with my CSS declarations) but will show up in the clipboard when it's copied there. I figured (wrongly?) that if I said "onHighlight, hide tooltip," maybe this wouldn't happen.
Though it may be more complicated, why not just have an onmousedown event on the <p> element, and thet event will then attach an onmousemove event and onmouseout event, so that if there is a mouse movement, while the button is down, then remove the class on the span elements, and once the user exits, the element then you can put them back.
It may be a bit tricky, and you may want to also look for key presses, or determine other times you want to know when to put back the css classes, but this would be one option, I believe.
It sounds like you need to go one step further and on highlight, remove the <span> and save a reference to it. Once the highlight is finished, re-insert the reference to the object.
// copy start detected
var savedTooltip = $('.tooltip').remove();
// later that day when copy finished
$('p').append(savedTooltip);
If position of the <span> in your markup is important, you'd have to create a temporary reference element so you'd know where to re-insert it in the DOM.
Related
I was wondering which, if any, framework would be the best to achieve capturing keystrokes and appending these to, say a "p" element. What I'm trying to achieve is having the client type something on the keyboard, and then have that sentence or whatever, appended to html, hereby displaying it in the "p" element.
Important notice; I'm not trying to call a function at a given keypress - ex. shift+alt, rather what I'm trying to do is, streaming the keyboardstrokes to an html element.
You don't necessarily need a framework for that task.
Another possbily viable option besides Kai Christensen's would be to create a textbox outside of the visible screen area, set the focus to this textbox automatically and create a change listener for the textbox.
You can then simply replace the content of the target element with the textbox's content whenever it changes.
This saves you the trouble of listening to keyboard events manually, distinguishing upper and lower case letters etc.
I would definitely go with jQuery to capture the keys and then constantly .replace() your div with a div with the same properties that contains the latest version of the updating string. Codecademy has a great set of lessons for this type of jQuery use.
Not that I'm an expert, though. I'm sure someone else has a better answer than my own.
Something like this?
Html
<p id="text"></p>
<input id="textinput"></input>
Js
//Register on keyup, append text from input-field to <p> element.
window.addEventListener("keyup", function () {
document.getElementById("text").innerHTML=document.getElementById('textinput').value;
});
jsfiddle: https://jsfiddle.net/vwobq9xf/1/
I have a slider and I've organized it into a <ul> with the <li>s as the individual items to cycle through. Only the second <li> is visible, and if you scroll left, it takes the last <li> and removes it, puts it on the front, and vice versa if you scroll right.
Here's the Code Pen: http://codepen.io/anon/pen/kGBcr
The slider is on the right side, under "More Influencers." The problem is when I click on the 'follow' link, it changes the CSS and text like I want only until it gets to a <li> that has been removed and placed on the other end. It doesn't prevent the default link, and just reloads the page.
Thanks for any help!
The functions that rotate the slider are on lines 120 and 135. The function that changes the text and CSS background color is on line 78.
Whenever you remove an element with remove(), it loses its bindings, and you need to re-bind event listeners.
However, if you use appendTo() instead to move it, the event listeners will stick around.
Another alternative is using the detach() function:
var objectWithEvents = $('#old').detach();
$('#new').append(objectWithEvents);
Your situation is even worse than just removing, since you're using:
html = $('element').html();
newHtml = someEl.append('<li>'+html+'</li>');
Around line 127. This makes it even worse because not only you're deleting the element, but taking its html content making it even more difficult to keep event listeners associated with it.
Here's an example http://jsbin.com/ukoqud/3/edit
If you click on a red box, you'll get an alert.
If you click on a link, everything in a blue box will be replaced with just a red box. Link will disappear and if you click on a red box then, you'll get no alert.
Why this happens?
Is it related to innerHTML?
Does it work the same way in all browsers?
Here's one more example http://jsbin.com/ukoqud/1/edit In this one you'll get an alert after clicking on a link. Things happen in a quite similar way, but result is different.
I would like to understand the reason, there's no need to fix my code.
When you call $(".red"), it returns a collection of DOM elements that exist at that moment. So $(".red").click(function...) just binds a handler to the click event on those elements. If you later create new elements with the same class, they weren't in this collection, so they don't have the handler bound to them. jQuery doesn't watch the DOM for changes and update the handlers dynamically -- the bindings are just on the elements you matched at the time you called click().
You either need to bind the handler again after adding the new HTML, or use delegation with .on():
$(".blue").on("click", ".red", function(){
alert('click on a red box detected');
});
This works by binding a handler to $(".blue"), which doesn't change dynamically. The handler checks whether the element you clicked on matches the ".red" selector, so it's able to handle dynamically-added elements without requiring rebinding.
I think the reason why it works in your second example is because the red block isn't inside the blue box to start. When you move it inside, jQuery reuses the same DOM elements, so the bindings go along with it. In the first example, the red box starts out inside the blue box. When you do $('.red').parent().html(...), the first thing it does is empty $('.red').parent() (the blue box), so the original red element is removed from the DOM, and its bindings are lost.
We need to understand how setting html of an element works. Then you will figure out your answer yourself.
Take a look at this bin Updated Bin
When we set HTML of an element, it first removes all the elements inside it.
Those elements are not removed from memory depending upon whether they are garbage collected or not.
If any of the child is having a reference, then that particular child won't be garbage collected.
In your case, we are having a reference to red element so it is still present in memory but not a part of document.
When we say blue.html(red) in my example, red element becomes a part of document again but this time there won't be any handlers on it So your click does not work.
While in your example2,
red element is always a part of document hence no handlers were lost when red element is moved inside blue element.
I hope this will help.
because when u click the link, you delete everything on screen and create everything from a scratch and event binding goes away. so you should use this
$(".blue").on("click", ".red", function(){
alert('');
});
this way, binding is done differently. it doesnt bind it statically
When a draggable attribute is enabled on a parent element(<li>) I cant make contenteditable work on its child element (<a>).
The focus goes on to child element (<a>),but I cant edit it at all.
Please check this sample
http://jsfiddle.net/pavank/4rdpV/11/
EDIT: I can edit content when I disable draggable on <li>
I came across the same problem today, and found a solution [using jQuery]
$('body').delegate('[contenteditable=true]','focus',function(){
$(this).parents('[draggable=true]')
.attr('data-draggableDisabled',1)
.removeAttr('draggable');
$(this).blur(function(){
$(this).parents('[data-draggableDisabled="1"]')
.attr('draggable','true')
.removeAttr('data-draggableDisabled');
});
});
$('body') can be replaced by anything more specific.
If new contenteditable elements are not added in the runtime, one can use bind instead of delegate.
It makes sense that the draggable and contenteditable properties would collide. contenteditable elements, like any text field, will focus on mousedown (not click). draggable elements operate based on mousemove, but so does selecting text in a contenteditable element, so how would the browser determine whether you are trying to drag the element or select text? Since the properties can't coexist on the same element, it appears that you need a javascript solution.
Try adding these two attributes to your anchor tag:
onfocus="this.parentNode.draggable = false;"
onblur="this.parentNode.draggable = true;"
That works for me if I add it to the <a> tags in your jsFiddle. You could also use jQuery if it's more complicated than getting the parentNode.
Note: This is a workaround since I believe the inability for these two functionalities to work together resides in the HTML spec itself (i.e. the not working together thing is intentional since the browser can't determine whether you want to focus or drag on the mousedown event)
I noticed you explicitly set 'no libraries', so I will provide a raw javascript/HTML5 answer
http://jsfiddle.net/4rdpV/26/
This was my crack at it.
First of all, it might be better to include the data in one single localStorage item, rather than scatter it.
storage={
'1.text':'test 1',
'2.text':'test 2'
}
if(localStorage['test']){
storage=JSON.parse(localStorage['test'])
}
this creates that ability, using JSON to convert between object and string. Objects can indeed be nested
I also added (edit) links next to the items, when clicked, these links will transform the items into input elements, so you can edit the text. After hitting enter, it transforms it back and saves the data. At the same time, the list items remain draggable.
After saving, hit F12 in chrome, find the console, and look in the localStorage object, you will see all the data was saved in localStorage['test'] as an Object using JSON.stringify()
I tried my best to design this to be scaleable, and I think I succeeded well enough; you just need to replace the HTML with a container and use a javascript for loop to write out several items, using the iterator of your choice to fill the parameter for edit(). For example:
Say you changed storage to hold "paradigms" of lists, and you have one called "shopping list". And say the storage object looks something like this:
{
"shopping list":{
1:"Milk",
2:"Eggs",
3:"Bread"
}
}
This could render that list out:
for(i in storage['shopping list']){
_item = storage['shopping list'][i];
container.innerHTML+='<li draggable=true><a id="item'+i+'">'+_item+'</a> (edit)</li>'
}
Of course, if you were to edit the structure of the storage object, you would need to edit the functions as well.
The output would look something like this:
Milk (edit)
Eggs (edit)
Bread (edit)
Don't worry about the input elements if that worries you; CSS can easily fix it to look like it didn't just change.
If you don't want the (edit) links to be visible, for example, you can do this in CSS:
a[href="#"]{
display:none;
}
li[draggable="true"]:hover a[href="#"]{
display:inline;
}
Now the edit links will only appear when you hover the mouse over the list item, like this version:
http://jsfiddle.net/4rdpV/27/
I hope this answer helped.
Using html5sortable and newer JQuery events (delegate is deprecated, answer 3 years after initial question), bug still affects Chrome 37. Contenteditable spans and html5sortable seem to play nice in other browsers. I know this is only partially relevant, just keeping documentation on changes I've noticed.
$(document).on('focus', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable('destroy'); // removes sortable from the whole parent UL
});
$(document).on('blur', 'li span[contenteditable]', function() {
$(this).parent().parent().sortable({ connectWith: '.sortable' }); // re-adds sortable to the parent UL
});
I'm creating a web-app interface which will allow users to construct sentences by clicking on words/phrases. Each word of phrase will be contained in its own page element, eg. a <div>. So the div class would need to allow:
dragging into a different order relative to the other divs/words (ie. sorting)
an X (only visible when hovering over) in the top right corner of its box to remove it completely upon clicking
changing of font/text upon click actions
be inserted into the page when the user wishes to add another word
For instance, imagine this is the page (quote marks denote an element):
"Hi, my name is" [Textbox] "I work at..." "and I was born in" [Combobox]
The phrase in italics is 'inactive' and not part of the sentence, but if the user clicked it then it would change to "I work at" [Textbox] and a new div would be added eg. "And also at..." to allow further expansion. The phrases in bold would have a close button /clickable action to get rid of them or make them inactive again. Also they would be draggable to change the order. As you can see I would also need to dynamically insert new textboxes and comboboxes to accommodate more phrases requiring input.
Could somebody give me a brief run down on what steps I need to take? I've seen sortable list elements in JQuery eg. http://jsfiddle.net/ctrlfrk/A4K4t/ which is a start. Would I just need to spruce this up with some basic JavaScript and CSS? Or would I need to use server-side scripting to dynamically add more page content?
To summarize, I need the div to change font/text upon clicking, have a hover-over close button, remove and be inserted upon simple click events, and be sortable by dragging. I also need comboboxes/textboxes to be added/removed in parallel.
Any tips would be greatly appreciated.
You don't need the server for new content unless it is stored or generated there.
Not having done this exact thing before I don't see any grand scheme to implement, I would just add the features incrementally until you get where you need to go. You may have to refactor things a bit along the way, but I probably don't have to tell you that. ;)
One thing you will have to do when you add or delete elements is to rebind the jQuery functions, it won't enough to just call them when the document loads. So put them in a function and call it whenever you add or remove an element.
Regarding your description of the UI behavior, you say that inactive elements can be dragged and that they can be made active by clicking on them. You can do this (just see if the thing has been dragged or not on mouseup to know whether it was drag or a click) but it might not be the best UI design choice, IMO. It wouldn't be the worst thing in the world, but I find it a little frustrating when the wrong thing happens when I try to do something. Of course, implementing and seeing for yourself is probably best.
Sorting:
I would implement the phrases in an unordered list (ul) which makes it as simple as
$("#ulId").sortable();
You can make ul/li items stack next to each other in a similar manner to how you make horizontal navigation menus out of ul elements.
A destrunction button:
Just use a template li similar to this
<li id="text1Wrapper">
<span id="text1"
onmouseover="javascript:$('text1Remove').fadeIn();"
onmouseout="javascript:$('text1Remove').fadeOut();"
onclick="javascript:$('text1Content').
replaceWith(
$(document.createElement('input'))
.attr('type','text')
.val($('text1Content').text())
);">
<span id="text1Content">Text Here</span>
<img id="text1Remove"
style="display: none;"
src="./x.jpg" alt="Remove"
onclick="javascript:$(this).parent().remove();"
/>
</span>
</li>
A quick description:
The span mouse over event tells jquery to fade in the remove button (so when you hover, the remove button becomes available).
The span mouse out event tells jquery to fade out the remove button when it's no longer needed (so when you move the mouse off the li, the remove button is no longer visible)
The onclick of the span replaces the span with a text box containing the content of the span. I will leave the "save changes" as an excercise for the reader, since it's essentially the same but in reverse.
Img tag style has display: none to hide it initially. This is essentially the end product of a fadeOut() but without the fade.
The on click event of the remove button gets the parent (the li) and removes it, and all children from the dom.
Note that the events are only put here because it seemed the logical place to explain it. All events in the outer SPAN tag are a useless waste of space, as they will all be overridden when we clone the node in the next section.
Insertion into the page:
All you have to do now is
var cloneLi = $('#text1Wrapper').clone();
cloneLi.attr('id', 'text2Wrapper');
var cloneSpan = cloneLi.children('#text1').attr('id', 'text2');
var cloneContent = cloneSpan.children('#text1Content').attr('id', 'text2Content');
var cloneRemove = cloneSpan.children('#text1Remove').attr('id', 'text2Remove');
You will then need to change the mouseover, mouseout and onclick functions of the outer span using jquery events:
cloneSpan.mouseover(function(e) {
// Insert functionality from template here
});
cloneSpan.mouseout(function(e) {
// Insert functionality from template here
});
cloneSpan.click(function(e) {
// Insert functionality from template here
});