I'm overlaying invisible text on top of an image. Is there a jQuery plugin (or similar) that will allow users to select an area on the image (which also selects the overlaid text) and be able to copy the content.
Right now, I have placed each character in its own <span /> tag. Problem is when user selects, it sometimes select all the overlaid text (unless user is extremely precise with his/her mouse), sometimes the image itself becomes selected, etc.
A solution similar to GMail's PDF viewer would be nice. Suggestions?
Google seems to know from a pdf where the various x,y text offsets are in the file. When you select a bunch of lines, it places a set of absolutely positioned "selection" divs over the image where the "text" is (they have class highlight-pane). When you select text, it fills in a #selection-content textarea with the contents of what you have selected, and selects the text in it (try using window.getSelection().anchorNode in Chrome, e.g.). Besides those selection overlays, there is just an image .page-image. I bet they actually use window to capture all the mouse gestures they care about (I assume mousedown and mouseup). (Here's an example pdf document)
If you're absolute-positioning the elements, you could detect mousedown, mousemove and mouseup, figure out the span elements mouse is over (or nearest to), and fill in a textarea with the contents of all content between those two elements. If you want to just use word-granularity, I doubt anyone would complain (surround each word with a span, rather than each letter).
Edit: I got kinda curious last night and coded up a really naive version. It only does mousedown and mouseup, and it doesn't work in IE (I don't feel like debugging it :)
Check it out on jsfiddle.
Features you might want to add:
Some better way of checking for position-based matches; I just do whether it's included in the box.
Dynamic updating on mousemove
Line-based rather than span-based
You could still do selecting by background color, but depending on how your elements are arranged it might not look very good. Also would need to support transparency.
Here's a simple example using my answer to your previous question: http://www.jsfiddle.net/yijiang/83W7X/2/embedded/result
var selected = [];
function drawSelection(){
if(selected.length){
selected.sort(function(a, b){
if(a.sourceIndex){
return a.sourceIndex - b.sourceIndex;
} else if(a.compareDocumentPosition){
if(a.compareDocumentPosition(b) == Node.DOCUMENT_POSITION_PRECEDING){
return 1;
} else {
return -1;
}
}
});
var range = rangy.createRange(),
sel = rangy.getSelection();
range.setStart(selected[0].children[0], 0);
range.setEnd(selected[selected.length - 1].children[0], 1);
sel.setSingleRange(range);
}
}
$('ul').selectable({
delay: 100,
selecting: function(event, ui) {
if(ui.selecting.getAttribute('class').indexOf('wrapper') !== -1 && $.inArray(ui.selecting, selected) === -1) {
selected.push(ui.selecting);
drawSelection();
}
},
unselecting: function(event, ui){
if(ui.unselecting.getAttribute('class').indexOf('wrapper') !== -1 && $.inArray(ui.unselecting, selected) > -1){
selected.splice($.inArray(ui.unselecting, selected), 1);
drawSelection();
}
}
});
It mixes jQuery UI's Selectable with Tim Down's excellent Rangy library to create something similar to what you asked for. I think. What you asked for wasn't exactly clear.
The code keeps an array of currently selected li elements. The second part of the code adds in the relevant event handlers and options. The drawSelection function is called every time an element is selected or deselected. The function first sorts all elements by their position in the DOM, then proceeds to draw a selection from the first selected li to the last.
The code, like theazureshadow's, is proof-of-concept only, since I'm abstracting what really should be the simple task of selecting the lis to the rather heavy Rangy library. It also does not perform very well and could do with some refactoring.
Related
I'm playing with cdk Drag and Drop cause I need it to create a POC for work.
I started with this code from the documentation website.
I saw that every time I drag an element outside its list, said element is hidden until I drop it in the same list or in another one.
So tell me if I'm wrong, but it seems that the dragged element is not a copy of the list item, but instead it's the element itself.
In conclusion I want the list to not change its layout while I'm dragging one of its elements.
Long story short:
CURRENT BEHAVIOUR
EXPECTED BEHAVIOR
I didn't found anything to do this in the cdk documentation.
In your stackblitz change in cdk-drag-drop-connected-sorting-example.css
change
.cdk-drag-placeholder {
opacity: 0;
}
into: (or just remove it)
.cdk-drag-placeholder {
/* opacity: 0; */
}
the fact its disappearing is due to css styling of the placeholder.
If you want to disable Sorting. Have a look HERE!
Also needed this and couldn't find something that the library supports.
My solution was to hide the whole original cdkDropList and replace it with a look alike.
It won't work for all, for me it works because each cdkDrag is in its own cdkDropList.
Dunno if you still need a solution for this, but there are 2 events that can help you create a solution for your problem.
cdkDropListExited / cdkDropListEntered
CdkDragEnter / CdkDragExit
The events mentioned are triggered when the dragged element is removed (which is what you wanna avoid) or when it has entered a container.
!NOTE These events are triggered when you move over the target container (but you click is still pressed) => you are still dragging
My solution was adding a custom placeholder div, which I hide/show when these events are triggered.
Hope this is clear enough for you
Peace
The expected behavior is still not implemented till now and there's an open issue on Github regarding this.
Someone provided a workaround for that and it actually worked for me.
Shortly speaking, the solution is adding the dragged item to the source list temporarily, until the item reaches is final destination, where he then removes the temp item from the source list.
Here's a link to the solution which worked for me:
https://stackblitz.com/edit/angular-krmecd
The key code to the solution
<mat-list
cdkDropList
[cdkDropListData]="sItems"
cdkDropListSortingDisabled
[cdkDropListEnterPredicate]="noReturnPredicate"
(cdkDropListExited)="onSourceListExited($event)"
(cdkDropListEntered)="onSourceListEntered($event)">
<mat-list-item *ngFor="let item of sItems" cdkDrag [cdkDragData]="item">{{ item.name }}</mat-list-item>
</mat-list>
noReturnPredicate() {
return false;
}
onSourceListExited(event: CdkDragExit<any>) {
this.sItems.splice(event.container.getItemIndex(event.item) + 1, 0, { ... event.item.data, temp: true });
}
onSourceListEntered(event: CdkDragEnter<any>) {
remove(this.sItems, { temp: true });
}
I've got a pretty simple problem whose solution turns out not to be that simple at all.
I want to add images in front of each option of a selectfield. To be more accurate, I want to add images to the picker it triggers, and also to the selectfield's current value.
For the sake of simplicity, I'll create a little example:
Let's say, you want a user to choose between one of the four playing card suits Diamonds, Hearts, Spades and Clubs. To support visual recognition, you want to prepend the corresponding symbol to each suit name, so it could look something like this:
My first choice of a sencha touch component, that enables selecting from a given set of options naturally was selectfield. Unfortunately, it only seems to be able to display pure text, and nothing more. After digging into the sencha touch sources, I finally came up with half a solution. Basically, I pass the selectfield a custom defaultPhonePickerConfig, in which the corresponding picker(that is used by the selectfield to display the options) gets assigned a custom itemTpl. The itemTpl does the rest, namely adding some html to display the image:
defaultPhonePickerConfig: {
listeners: {
initialize: function() {
var slots = this.query('pickerslot');
Ext.each(slots, function(slot) {
slot.setItemTpl('<img src="someImage.jpg"> {text}');
});
},
change: function() {
// reconstruct the selectfield's change handler,
// since it gets overwritten
var iconSelect = Ext.Viewport.query('#IconSelect')[0];
iconSelect.onPickerChange.apply(iconSelect, arguments);
}
}
}
A working fiddle for this solution can be found here.
My solution isn't that bad, but there's a slight cosmetical problem, that's just not acceptable to me: The icons are only displayed in the picker (lower part of the screenshot above), but not the selectfield itself (upper, grayed out part) when the option was selected. And there seems to be no good way to add an icon to the selectfield's current value aswell.
And that's the main concern of my question: What good way is there to add an icon to both the picker's options and also to the selecfield's current value? Do I maybe just have to add relatively little code to my existing solution or should I take a whole nother approach?
Every contribution is appreciated. Thank you!
Update:
After some hacking around, I found a way (an ugly one) to prepend an icon to the selectfield itself. It is mostly based on brutal HTML DOM manipulation: I now also define event handlers for the selectfield itself (change and painted). On initialization and every time the value is changed, my handlers search the generated DOM for the underlying <input> and mess around with it. (That bad boy is probably the reason why we can't assign HTML in the first place, since the framework changes its value attribute. And value on the other hand can only contain plain text.)
This is how I define the selectfield's listeners:
listeners: {
change: function () {
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(arguments[1], pickerDOM);
},
painted: function () {
// Initialize an icon on creation
var pickerDOM = document.querySelector('#' + this.getId() + ' input[name="picker"]');
PickerIcons.app.prependIconToSelectfield(this.getValue(), pickerDOM);
}
}
The corresponding function prependIconToSelectfield() just defines some CSS:
prependIconToSelectfield: function (optValue, domElement) {
var iconUrl = this.getIconUrl(optValue);
domElement.style.backgroundImage = 'url(' + iconUrl + ')';
domElement.style.backgroundSize = '20px 20px';
domElement.style.backgroundRepeat = 'no-repeat';
domElement.style.backgroundPosition = 'left center';
domElement.style.paddingLeft = '30px';
}
Check out this fiddle for a working example.
This is still no good solution to me, since doing this kind of hackish DOM manipulation is way too rogue for my taste. I don't know what the side effects of actively messing around in the DOM could be in bigger projects, and don't want to learn it the hard way. So, I'm still looking for a cleaner solution.
First kudos on working so hard sencha touch is extremely hard to manipulate when you try to do something out of the box. Having said that let me try & propose a solution for what you want.
A selectfield in sencha has the following DOM tag structure.
div.x-field-select
div.x-field-input
input.x-input-el
div.x-clear-icon
div.x-field-mask
Now concentrate on the x-clear-icon it is normally hidden since a selectfield does not need a clear button. First write a css class for it to show it(display: block). This would display it with an X button similar to text field & it will be positioned towards the right corner. You can through css position it to the left and on change of the select field you can change its background to what you want. It is not a very straight forward solution but i have tried it for a similar problem & it works. Judging from what you have done above i think you can do it. All the best.
Hope my solution helps.
I took a peek at the source of http://wonderwall.msn.com and noticed how all the span tags that the blocks of the wall have don't seem to be associated with any ID. It makes me very curious how they are able to accomplish the animated repositioning of elements when you click on one of the blocks/images without associated ID.
I am curious how you can click on say an image and get other images around it to move to the side. Is it some sort of formula or algoirthm?
I would like to accomplish getting say, 5 spans/blocks, clicking on one, and getting others to animate/move to the sides.
IDs are not necessary and often harmful. You don't need them, generated or otherwise.
When you put an element on a page with an ID, you're making the claim that there should be only one of whatever it is. Seldom is this true. More often, what you want to do is associate some behavior with some of the elements on the page, of which there may be many, one or zero.
In this case, there are lots of little image dealies, which when clicked, rearrange themselves. I don't have an algorithm for you for calculating how they should move, but here's a framework for how you could achieve the same with jQuery.
// create jQuery plugin for highlighting and shuffling brick dealies
(function($){
function expandify() {
var href = this.attr('href');
// create a popup containing the href
return this;
}
function shuffle() {
this.each(function(index, elem){
// calculate new position and move the element there.
});
return this;
}
$.fn.expandify = expandify;
$.fn.shuffle = shuffle;
})(jQuery);
// attaches behaviors to elements on the page after they've loaded
// either $.ready, or window onload, or after some ajaxing takes place
$('.wallBrick')
.click(function(e){
$(e.target)
.expandify();
$('.wallBrick')
.not(e.target)
.shuffle();
});
The IDs are generated via JavaScript on-the-fly. You won't see it in the source, but you'll see it if you inspect it with Firebug.
I have a <textarea> that I want to grow by one row every time the user enters into the last row shown in the <textarea>. Is this possible?
I have seen it done before, but can't figure out how to do it.
Okay, I just created this off the top of my head, so there may be browser compatibility issues. The concept is pretty simple however. Basically, on every keyup event, append a newline to the current text in the textarea element, then check if scrollHeight is greater than offsetHeight. If this is the case, set the elements height to be equal to scrollHeight. After all this, remove the appended newline. Assuming your textarea element has the id ta:
EDIT -- Okay, my original idea caused the cursor position to jump to the end of the text even if the user moved the cursor back, not good. The fix I came up with involves creating an invisible clone of the text area, and inserting the extra newline in that one, as to not disturb the cursor position in the visible one. Not sure I'm enterily comfortable with this method, but it works for now. If there is a better method I'd be happy to hear it. Here's the updated code:
var shadow = document.createElement('textarea');
shadow.className = 'autosize';
shadow.style.visibility = 'hidden';
document.getElementById('ta').onkeyup = function() {
this.parentNode.appendChild(shadow);
shadow.value = this.value + '\n';
if (shadow.scrollHeight > shadow.offsetHeight) {
this.style.height = shadow.scrollHeight + 'px';
}
this.parentNode.removeChild(shadow);
};
And the updated test http://jsfiddle.net/7wezW/1/
Now back to your regular programming...
Works well in Chrome, if someone points out issues in other browsers, I will try work them out.
PS, I should point out that if the user pastes text using just the mouse, the size of the textarea element will not adjust. Should be a trivial issue, but I'll leave it out as not to over-complicate things.
There are code and a demo at http://webdesign.torn.be/tutorials/javascript/prototype/auto-expand-contract-textarea/.
There's also a non-IE6 version that doesn't use frameworks:
http://scrivna.com/blog/2008/04/12/javascript-expanding-textareas/
Yes, there are many plugins for JQuery, such as this one, which comes with a demo: http://james.padolsey.com/javascript/jquery-plugin-autoresize/
Very customizable in terms of maximum height, buffer space, etc.
I am working on an in-place HTML editor, concentrating on Firefox only right now. I have an element inserted where the cursor should be and also have left and right arrows working, but I can't seem to find a way to find:
Start and end of a line for the home and end keys
The next line up or down for the up/down arrows.
I see document.elementFromPoint, but this doesn't get me a Range object. The Range object itself seems rather useless when it comes to using pixel positions.
If you need a to create a range for the element under specific pixel position, you can combine document.elementFromPoint() and document.createRange() and Range.selectNodeContents();
The snippet below would highlight the content of an element at (100,200)
var elem = document.elementFromPoint(100,200);
var r = document.createRange();
var s = window.getSelection()
r.selectNodeContents(elem);
s.removeAllRanges();
s.addRange(r);
I hope this will help you find the final solution.
I'm not sure what you mean by 'start and end of a line'. Are you referring to a line of text on the page, regardless of its containing element? Can you provide a link to your app or an illustrative example page?
IE and Firefox both provide an element.getClientRects() method. However, it only works under certain circumstances and doesn't behave the same from browser to browser. See quirksmode for details.
This method will probably not be helpful in many situations. In the case that you have a P element containing bare text, Firefox returns a collection containing a single element representing the P element. It doesn't tell you anything about the lines of text it contains.
Most of the in-place editors that I've seen work by clicking on an element to edit it. The element is then replaced by a textbox/area and associated save/cancel buttons.