Focusing on nested contenteditable element - javascript

So, I have two contenteditable divs nested inside of another:
<div class="top" contenteditable="true">
<div class="nested" contenteditable="true">This</div>
</div>
Here is Fiddle.
When it is focused, nested should be focused but using console.log(document.activeElement); it shows that the top is focused and it doesn't recognize the nested div.
In a case where content is being edited, I need to recognize nested div element instead of the top element.
How would I achieve this? Any help will be much appreciated.

The way [contenteditable] elements are handled by browser made any nested [contenteditable] not handling any event, the editing host is the former editable parent. See spec:
If an element is editable and its parent element is not, or if an
element is editable and it has no parent element, then the element is
an editing host. Editable elements can be nested. User agents must
make editing hosts focusable (which typically means they enter the tab
order). An editing host can contain non-editable sections, these are
handled as described below. An editing host can contain non-editable sections that contain further editing hosts.
Now as a workaround, you could make focused nested editable element the hosting host by setting any of its editable parent temporaly not editable. See e.g:
$('div.top [contenteditable]').on('focusin focusout', function(e) {
$(this).parents('[contenteditable]').prop('contenteditable', e.type === "focusout");
});
-updated jsFiddle-

Give tabindex=-1 to the nested div, than can be focused:
.nested {
display: inline;
background-color: #eef;
}
<div class="top" contenteditable="true">
Editable div <div class="nested" tabindex=-1>Nested div</div>
</div>
Notes:
contenteditable is inherited, so there is no need to specify it again.
it only works with mouse focus. Moving the caret (cursor left/right) over the nested div, will not focus the nested div. Similarily, leaving a focused nested div with the caret, will not does not give the focus back to the parent div. Handling might need work arounds with keydown listener, range and selection.

For nested contentEditable elements, focus shall be only triggered in the top ancestor.
You could use selection to get the closest element where the caret is right in:
function getCEOfCaret(){
const selection = window.getSelection()
const range = selection.getRangeAt(0)
const start = range.startContainer;
let startElement;
if(start.nodeType === 1) startElement = start;
else startElement = start.parentElement;
return startElement.closest('[contenteditable="true"]')
}
console.log(getCEOfCaret())

Related

Content editable DIV with BREAK's or just DIV's

I have a div that is content editable and I use this div as an Input for my code. The problem is the user input. If a user writes something in the div, it's just a normal text, but if he makes a new line there is in a div. Can I somehow make it that the first line is also in a div? Or is it possible to just make break elements if there is a new line?
By the way, I want to use a content editable div, because I want to color some parts of the text different that others, why I can't use a text area.
<div contenteditable="true">
"1"
<div>2</div>
</div>
As you can see in the code below if you set the first child element tag as a div, each added child will be a div as well.
var div = document.querySelector("div[contenteditable=true]");
div.addEventListener('input', (e) => {
console.log(e.target.children)
});
console.log(div);
<div contenteditable=true>
<div>‎</div>
<div>
Edit: To have any character at the beginning and be able to click it, copy the invisible character in the first child div.
or copy it here: ‎

Select every element that isn't a parent to any elements

I am developing a game that glitches at some point through using the CSS filter: invert(1); property. However, when you use that property on body, it makes everything position: absolute;. This is not good because I need most elements to be fixed, and everything goes to a negative top and not visible. How can I effectively get all elements in a list that isn't a parent to any other elements, but included if it has text? Any answers or other stack overflow topics would be nice!
Here is some of my code:
// In a working loop called Repeat()
if(Glitch == 1) {
document.querySelector(".ChangableStyles").innerHTML = "* {filter: invert(1)}"
} else {
document.querySelector(".ChangableStyles").innerHTML = ""
}
Edit: Since all of you are asking, the .ChangableStyles tag is a style element. The filter on everything applies when I change the innerHTML of that style tag to valid CSS styles. I don't want to be rude, but I have the .ChangableStyles thing figured out. Thank you.
You mention you already have a list of elements, but it's not clear how you're generating that list. I've gone ahead on the assumption you're wanting to "select" all elements in <body></body> that don't have any children.
You can use a combination of Array.from(), your pre-existing selection logic, and a filter() using node.childElementCount === 0 to accomplish what you describe. However on higher-complexity DOMs this will be computationally expensive, so I would implore you to re-consider your design instead of opting for this route. To be clear, this will meet your requirement of selecting ANY Node in the DOM which has no child elements ("isn't a parent to any other elements"), which includes any script, style or other "user-invisible" nodes in the body.
document.getElementById('get-elements-button').addEventListener('click', function () {
console.log(Array.from(document.body.getElementsByTagName("*")).filter(function (node) {
return node.childElementCount === 0;
}));
});
<div class="has-child-elements">
This is a child element
</div>
<div class="has-no-child-elements">
</div>
<div class="has-child-elements">
This is also a child element
</div>
<button id='get-elements-button'>Get elements with child elements →</button>

Select text in the last div of class

I have some contenteditable divs with the same class, the div is dynamically created so I don't know how many there are.
<div class="scale1" onclick="document.execCommand('selectAll',false,null)" contenteditable>0</div>
<div class="scale1" onclick="document.execCommand('selectAll',false,null)" contenteditable>0</div>
<div class="scale1" onclick="document.execCommand('selectAll',false,null)" contenteditable>0</div>
I want the text in the last one to be selected (like if you hold the left mouse button down over the text) with a button
The obvious way in jquery:
$(".scale1:last").click();
Doesn't work, it selects the hole page.
I also thought about ways in javascript like Selection.selectAllChildren and Selection.addRange() but i have no elegant way of knowing the last div
How to make text selection checkout here
To reach your goal - replace ID selection with following code:
var query = document.querySelectorAll(".scale1"),
text = query[query.length-1];
Try:
$('.scale1:last').text()
or
$('.scale1').last().text()
Either would work, even for dynamically added elements.
DEMO

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.

Select HTML Elements Visually

An HTML webpage is rendered in div. How can I allow the user to click and select any HTML tag? Similar to how Firebug and Chrome does it. I need the selected tag returned as is.
Add an event listener on your div and check for the event's target property (srcElement for IE).
document.getElementById("page").onclick = function(e) {
var target = e.target || e.srcElement;
alert(e.target.tagName);
};
http://jsfiddle.net/Xeon06/e67qW/1/
In jQuery:
$.click( function(){
var clicked = $(this);
});
you can add a onclick attribute to each html element which returns itself.
Chrome and Firefox also have a hover which outlines the element tough. To make that in a easy (and ugly) way you could add a hover css pseudo class for the html elements which adds a border of 1px to the html element.
*:hover{
border: 1px solid;
}
A better way would be to create a new element with javascript with the same measurements and position and to give it a z-index so it floats above the existing element

Categories