How to get the target element for an 'selectionchange' dom event - javascript

I find myself in a situation where I want to get the target element that triggered the selectionChange dom event.
But judging by https://developer.mozilla.org/en-US/docs/Web/Events/selectionchange, it seems that the element in e.target is always the document object in the normal (non-input, non-textarea) case.
So does that mean I will have to manually call window.getSelection, and figure out the current cursor location and find the dom node that way?
Does anyone know of a short cut? or some working example would be nice.

If your element can become an active element, use document.activeElement to get the element we're selecting inside of. This will work with textareas, inputs, and on elements whose tabindex is set.
// NOTE: activeElement can only be found on selectable elements!
document.addEventListener('selectionchange',function(){
document.getElementById('currentTag').innerHTML = document.activeElement.tagName;
});
#currentTag{
font-weight:bold;
}
<textarea>This is sample text you can replace and move your cursor within. Whee!</textarea>
<br><input type="text" placeholder="Input">
<p tabindex="0">P tag text is the best tag text. <span color="blue">Unless you have a span.</span></p>
<ol tabindex="0">
<li tabindex="0">Item 1</li>
<li tabindex="0">Item 2</li>
</ol>
<p id="currentTag"></p>

You can use document.selection to get what is currently selected.
Example taken from http://help.dottoro.com/ljixpxji.php
<head>
<script type="text/javascript">
document.onselectionchange = OnChange;
function OnChange () {
var range = document.selection.createRange ();
if (range.text.length == 0) {
alert ("The current selection is empty");
}
else {
alert ("The contents of the current selection are\n" + range.text);
}
}
</script>
</head>
<body>
Select this text on this page!
</body>
--EDIT--
As pointed out by #user1017674 this code does not work in chrome, after a little bit of research I found document.selection should only be used in IE < 9. It seems that window.getSelection() is still going to be the best way to get it.
Ref. Does chrome supports document.selection?

Related

Clicking <span> element > JavaScript output document.activeElement VS. event.target

I’m newbie in JavaScript. Here’s my code below.
Upon clicking a button, I understand why event.target returns span object (because that’s the innermost element I click. correct?).
My doubt is, going by same logic why document.activeElement returns button object and not span object? Isn't it the span element that should be active when I click the button or not?!
Thanks in advance for your clarifications :=)
<!DOCTYPE html>
<head>
<script type="text/javascript">
function GetActive () {
if (document.activeElement) {
var output = document.getElementById ("output");
output.innerHTML = document.activeElement + ’-’ + event.target
}
}
</script>
</head>
<body onclick="GetActive ();">
Click anywhere on the page to get the active element <input id="myInput" value="input field" />
<button> <span>Sample button</span> </button>
<div id="output"></div>
</body>
This happens because document.activeElement reports the element which is currently focused (or will receive keystrokes).
Returns the currently focused element, that is, the element that will get keystroke events if the user types any. This attribute is read only.
Since elements like span and div can't receive keystrokes or be focused on (by tabbing to them) ordinarily, they won't ever be the activeElement. Those tags will be the activeElement only when they can receive keystrokes or be "active", such as when you've made them contenteditable or given them a tabindex.
Demo

How to get the <p> where the cursor is in?

I have a DIV element with contenteditable="true".
As i write text into this div i get multiple elements.
Here is an example link.
Question
How can I get the element which contains the cursor?
And it would be extra nice to be able to obtain the index position of the cursor (in a cross-browser supportive way).
Thanks!
Updated
By cursor index position, i don't necessarily mean X, Y coordinates. I mean string index position of a sentence, for instance.
Do you need something like that?
HTML
<section contenteditable="true" id="editable">
<h2>Go ahead, edit away!</h2>
<p>Here's a typical paragraph element</p>
<ol>
<li>and now a list</li>
<li>with only</li>
<li>three items</li>
</ol>
</section>
JS Code:
$('#editable').mousemove(function(e){
console.log(e.target.nodeName);
console.log(e.pageX + ", " + e.pageY);
});
NOTE: You could also change mousemove event to what ever you want, as an example: click, hover etc.
I think usual nodes (basically non input nodes) do not have a focused flag. But before you can write into an element you have to do a click into it. You can hook into the click event to get the node.
(e.g. with jQuery:
$('document').on('click', function() {
console.log('you have clicked on: ', $(this));
});
)
You could add onMouseOver attributes to containers, making them run a code that changes some variable for example. You could use also onMouseOut attributes to remove the value from the variable when the mouse exits the container.
This is a sample of that code:
HTML:
<!doctype html>
<html>
<head>
<title>Example page</title>
<script src="your/script/path.js"></script>
</head>
<body>
<p id="1" onMouseOver="setParagraph(this)">Example paragraph 1</p>
<p id="2" onMouseOver="setParagraph(this)">Example paragraph 2</p>
</body>
</html>
Javascript:
var mouseOverParagraph,
setParagraph = function (el) {
'use strict';
mouseOverParagraph = el.id;
}
I would imagine doing something like:
var hovered;
$('div[contenteditable] *').bind('mouseenter.editable', function(e) {
e.stopPropagation();
hovered = this;
console.log(this);
console.log($(this).index());
console.log(e.pageX);
console.log(e.pageY);
});
Just have a global variable that gets changed upon hover, stopPropagation() to make sure no parent element "takes the credit", and you get to play with this, e.pageX, e.pageY with ease.
You can use the jQuery focus selector to find which element has the cursor focus:
var focusedP = null;
$(document).on("focus","p[contenteditable]",function(){
focusedP = $(this);
})
$(document).on("blur","p[contenteditable]",function(){
focusedP = null;
})
JSFiddle
As for cursor position within the element, I can't say, but you might be able to work out what character was originally clicked by capturing the mouse click and getting the relative coords. With a static font size and line height, you could calculate from the coords what line and character it was at.

Backbone.js – changes to li element don't display immediately

What I have in my template is just a bunch of divs and a list, consisting of multiple li elements. The use case is simple, the li elements are a dropdown and are displayed only on clicking a button. When the dropdown is visible and someone begins to type, the matching li element should be selected, or there should be a visual indication.
My approach is this, on a keyup event, I look for the typed word (this is quite easy) in the li elements. I find a few elements, which I've confirmed. Now, when I try to do something with these elements, nothing seems to happen WHILE the dropdown is open (right now, I'm trying to .toggle()) these elements. Now, when I click the button again (which showed the dropdown in the first place) (this click hides the dropdown), and then click the same button again to reveal the dropdown, voila! The values have been changed as they should be – the matching elements have been hidden/shown.
This has me stumped. For company policies, I can't upload the code up here, but I'll be very thankful if someone else has had this problem before and can help me out.
EDIT:
Code: function to change the dropdown on keypress, this is being fired correctly:
filterOptionsForKeypress: function (event) {
var typedString = this.$('input.filter-button-text').val(),
searchToken = _.trim(typedString.toLowerCase().replace(/ /g, '_')),
matchingLi = this.$("li[data-field^='" + searchToken + "']", this.$el), // makes no difference with or without the context, this.$el
that = this;
if (matchingLi && matchingLi.length) {
this.$(matchingLi[0]).html('kaka'); // this change shows only if the dropdown is hidden + shown again
console.log('trying to move focus', this.$(matchingLi[0]).attr('data-field'));
}
// this.$el.append('Some text'); -- this works, I see the changes as they happen
}
And the template looks something like this:
<div class="filter-button filter-option {{if !model.include}}button-red{{else}}button-green{{/if}} toggle-dropdown" data-dropdown-class="{{if !model.include}}button-red{{else}}button-green{{/if}}">
<div class="filter-button-text">${model.option}</div>
<div class="filter-drop"></div>
<div class="dropdown filter-dropdown">
<ul>
{{each model.config.options}}
<li data-field="${$value.op}" data-include='${$value.include}'>${$value.name}</li>
{{/each}}
</ul>
</div>
</div>
EDIT #2:
When the dropdown is open, this is how the html looks:
OPEN:
CLOSED:
So basically, apart from adding a few styles to the enclosing div and a class 'open', I don't see any differences.
The problem was that we're using a plugin for dropdown menus. So basically, what we saw on the screen wasn't what we found selecting with this.$(). The solution? Look globally and with a :visible filter voila, problem solved.

What javascript method to target descendants?

I'm using a javascript function to set the value of a text field, based on the option chosen from a select field.
The javascript contains a lot of other stuff, but only the following is relevant to this question.
$(function (){
$('.source').live("change", function(e) {
var target = $(this).next('.target')[0];
----other stuff----
});
});
I originally had my form set up as follows, and everything worked fine.
<select class="source"></select>
<input class="target"></input>
I've subsequently added some styling, which has required extra divs.
<select class="source"></select>
<div class="level1">
<div class="level2">
<input class="target"></input>
</div>
</div>
Now the javascript function does not work, because the next method only targets siblings and not descendants.
So my question is, what method should I be using to target a specific descendant?
An important fact: this markup is part of a nested form, and is repeated several times on the same page. It is important that the function targets the correct .target field, i.e. immediately subsequent and descendant.
I've tried obvious candidates – .find(), .children() — but these don't seem to work. Would appreciate any ideas or pointers.
Thanks!
Now that in the new markup structure .target input is wrapped in a div with class level1 you can find that div first using next() and then use find() method to get to the .target input.
$(function (){
$('.source').live("change", function(e) {
var target = $(this).next('.level1').find('.target')[0];
----other stuff----
});
});
Note: Even if you don't pass any selector to next() also it will work fine because it only selects the immediate next sibling optionally filtered by the selector which we pass.
In your case this would work:
$(function (){
$('.source').live("change", function(e) {
var target = $(this).next().find('.target')[0];
----other stuff----
});
})
;
It's a descendant of the sibling, so this should do the trick:
var target = $(this).next('.level1').find('.target')[0];

Use JQuery to listen for all clicks on the html body Except a specific div and its children

I am trying to use a jQuery listener to listen for a users clicks on the html body and perform a specific function if anywhere on the body has been clicked except for a specific div and the children within that div.
The idea is that the div is a popup type element and instead of having to have a close button that the user can click, they should just be able to click anywhere on the page besides that div and it will automatically close.
I have been using this listener:
var initialClick = false;
$('body').on('click.addPhoneListeners', function(event) {
var target = EventUtility.getTarget(event);
if(initialClick) {
if(target.parentNode.id != clone.id && target.id != '') {
finish();
};
}
initialClick = true;
});
Which listens for all clicks on the body and unless the click comes from within the element I want the user to be able to interact with, it closes. Unfortunately this only works with a div that has only one level of children. As soon as I start getting multiple hierarchies such as this:
<div id="addressContainer">
<div id="address" class="hidden row">
<div class="row">
<div id="address.primary" class="hidden">P</div>
<div id="address.type"></div>
<div id="address.street"></div>
<div id="address.editButton" class="hidden"><a id="addressEditButton">Edit</a></div>
<div id="address.deleteButton" class="hidden"><a id="addressDeleteButton">Delete</a></div>
</div>
<div class="row">
<div id="address.city"></div>
<div id="address.state"></div>
<div id="address.zip"></div>
</div>
<input type="hidden" id="address.id"></input>
</div>
</div>
The target.parentNode.id gives me the objects parent element as opposed to the addressContainer id and thus does not work. Is use the top level parent from within nested elements? Other elements will be using this same code, so it has to work on both divs with just one level and div's with multiple.
UPDATE: Found a few excellent solutions, thanks guys. I do however have one other question. Refer to my code above where I set an initialClick boolean to false, then set it to true. I am doing this because for some reason if I don't, when I go to add the popup div, the initial click from the button used to set that popup fires the listener and closes the popup before I have a chance to do anything. This has been my solution around the problem, but is that the only way? Or am I just setting the listener slightly incorrect?
I usually do something like this:
$(document).click(function(e) {
// hide popup
});
$('#popup_div').click(function(e) {
e.stopPropagation();
});
That way the clicks from your popup never propagate to the document, so the close function never fires.
Replace
if(target.parentNode.id != clone.id)
with
if ($(target).closest("#" + clone.id).length === 0)
(I left the second clause alone since it didn't seem related to your question.)
This tries to find the closest ancestor with ID equal to clone.id. If none is found, an empty jQuery object is returned (i.e. one with length === 0), which is what we test for.
Incidentally: jQuery normalizes event.target, so you can just use that instead of whatever custom monstrosity EventUtility.getTarget(event) embodies.

Categories