Is it possible to select text from multiple elements? - javascript

Using a java script, you can make a selection. This will be equivalent to selecting with the mouse cursor. The code that I added to the question makes a selection of each element inside the container you click on, but the previous selection disappears. Can this be changed?
const selectElement = (element) =>
{
const range = document.createRange();
range.selectNode(element);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
const container = document.getElementById("container");
container.addEventListener('click', (event) =>
{
const x = event.pageX;
const y = event.pageY;
selectElement(document.elementFromPoint(x, y));
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Code snippet</title>
</head>
<body>
<div id="container">
<div id="firstElement">
First element text
</div>
<div id="secondElement">
Second element text
</div>
</div>
</body>
</html>
EDIT:
For better understanding, I added a second snippet that does what I would like to get, but without using a window.getSelection().addRange().
const selectElement = (element) =>
{
element.style.backgroundColor = 'blue';
element.style.color = 'white';
}
const container = document.getElementById("container");
container.addEventListener('click', (event) =>
{
const x = event.pageX;
const y = event.pageY;
selectElement(document.elementFromPoint(x, y));
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Code snippet</title>
</head>
<body>
<div id="container">
<div id="firstElement">
First element text
</div>
<div id="secondElement">
Second element text
</div>
</div>
</body>
</html>

Accpording to the other answer and MDN it only works in FF.
https://stackoverflow.com/a/70175542/4860688
https://developer.mozilla.org/en-US/docs/Web/API/Selection/addRange#example
But, here is how to do it in FF:
Moving focus and/or clicking on elements typically (always?) removes the previous selection as per pretty standard UI selection behaviour.
If you wanted to retain the previous selection range(s) then you will need to store them separately and re-add them to the window's selection ranges.
Obviously this is counter intuitive to any user expectation but I'm not going to presume anything about why you want this :)
var ranges = [];
const selectElement = (element) =>
{
const range = document.createRange();
range.selectNode(element);
ranges.push(range);
const selection = window.getSelection();
ranges.forEach(r => selection.addRange(r));
}
const container = document.getElementById("container");
container.addEventListener('click', (event) =>
{
selectElement(event.target);
});
<!DOCTYPE html>
<html lang="en">
<head>
<title>Code snippet</title>
</head>
<body>
<div id="container">
<div id="firstElement">
First element text
</div>
<div id="secondElement">
Second element text
</div>
</div>
</body>
</html>
Note that if you click outside your container div, the selections are cleared - this is the standard behaviour and since your event listener is only on the div, the selections are not restored. If you want to change that, move your event listener to the body.

Related

How for loop work with JavaScript EventListner

I have a html document where has 3 button and one h1 element. I have tried to change the h1 element content by clicking in each button with EventListener. And i have done this too. But i dont understand how foor loop work there. How exact button working when i am click on them?
//JavaScript Code
var len = document.querySelectorAll(".mybutton").length
for (let i = 0; i < len; i++) {
var doucemnts = document.querySelectorAll("button")[i]
doucemnts.addEventListener("click", function(){
var text = this.innerHTML
document.querySelector("h1").innerHTML= "You Have Selected " + text
console.log(i)
})
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>You have selected no button</h1>
<button class="mybutton">Button 1</button>
<button class="mybutton">Button 2</button>
<button class="mybutton">Button 3</button>
<script src="index.js">
</script>
</body>
</html>
Can Anyone Tell me How For loop worked there?
For in the nodelist attach an event listener that calls a function when the button is clicked. The function updates the h1 with the button text.
Your code can be improved slightly by caching all the elements up front, and then using those references in the loop (a for/of loop is a little easier to parse) instead.
// Cache the elements
const buttons = document.querySelectorAll('.mybutton');
const heading = document.querySelector('h1');
// Loop over `buttons`
for (const button of buttons) {
// For every button add a click listener
button.addEventListener('click', function() {
// Assign the button textContent to a new variable
const text = this.textContent;
// Use that variable to update the textContent
// of the heading textContent
heading.textContent = 'You Have Selected ' + text;
});
}
<h1>You Have Selected No Button</h1>
<button class="mybutton">Button 1</button>
<button class="mybutton">Button 2</button>
<button class="mybutton">Button 3</button>
You could even use event delegation. It attaches one listener to a parent element which captures events from its child elements as they "bubble up" the DOM.
// Cache the elements, and a partial string
const heading = document.querySelector('h1');
const buttons = document.querySelector('.buttons');
const tmpl = 'You Have Selected';
// Add an event listener to the buttons container
buttons.addEventListener('click', handleClick);
function handleClick(e) {
// If the child element that fired the
// click event is a button
if (e.target.matches('button')) {
// Construct the new string, and assign it
// to the heading textContent
const text = `${tmpl} ${e.target.textContent}`;
heading.textContent = text;
}
}
<h1>You Have Selected No Button</h1>
<section class="buttons">
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
</section>
Additional information
matches
Template/string literals
In JS when manipulating DOM elements, it's better to query elements once at start of your code then reuse those variables. Another important thing to consider is to use const/let instead of old var keyword. Here's your updated code based on that and might make more sense now:
const h1 = document.querySelector('h1');
const buttons = document.querySelectorAll('.mybutton');
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", function() {
const text = this.innerHTML;
h1.innerHTML = "You Have Selected " + text;
});
}
This code is basically doing the same, but instead of querying for elements on each iteration, it's done once before starting loop. Then loop over the buttons one by one and add click event to each one of them.
You can also use for..of it might be easier to understand. Here's the code updated to use for..of:
const h1 = document.querySelector('h1');
const buttons = document.querySelectorAll('.mybutton');
for (const button of buttons) {
button.addEventListener('click', function() {
const text = this.innerHTML;
h1.innerHTML = 'You Have Selected ' + text;
});
}
Update: The main thing to understand in both code snippets is const text = this.innerHTML. This line of code is using the this keyword to get the HTML content of each button. this keyword will be pointing to button triggered the event, so based on that you're getting text displayed on that button using innerHTML property and concatenate that to content of h1. In short, getting text in clicked button and append to h1 content.
I have removed console.log(i) as it refers to variable i that is valid only while add the event listener callback only. It'll show an error because when event callback function is called there will be no for loop and i won't be defined.

Clone div and traverse to whats inside

I have a div with an input inside it, i want to clone this div on click with different id, and i want the id of the input inside it to change also, and i want to limit the number of clones,
var div = document.getElementById('clonedDiv'),
clone = div.cloneNode(true);
clone.id = "some_id";
document.body.appendChild(clone);
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button type="button">Clone me</button>
how can i do that? here is my code:
I'd like to offer a native JS solution to your problem. It is rather straight forward and works in all modern browsers.
outerHTML is IE4+, see here
insertAdjacentHTML is IE4+, see here
const
sourceDiv = document.getElementById('clonedDiv'),
cloneTrigger = document.getElementById('make-clone'),
maxClones = 3;
let
clonesCreated = 0;
function makeClone() {
// Make sure there aren't too many clones created.
if (clonesCreated === maxClones) {
console.log('max clones reached');
return;
}
let
// outerHTML is llke innerHTML but includes the element itself.
clone = sourceDiv.outerHTML;
// Replace the two IDs for unique IDs
clone = clone.replace('clonedDiv', `clonedDiv_${clones}`);
clone = clone.replace('clonedInput', `clonedInput_${clones}`);
// insertAdjacentHTML is like innerHTML except your can tell where it should be inserted in relation to the element.
// In this case, add the clone before the button element.
cloneTrigger.insertAdjacentHTML('beforebegin', clone);
// Increase the number of clones created.
clonesCreated++;
}
cloneTrigger.addEventListener('click', makeClone);
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button id="make-clone" type="button">Clone me</button>
Since you have tagged jQuery in the question, we can use it to greatly simplify things.
Bind a click event handler to the clone button, and we can use it to call a method, say clone(), that will handle all the logic of cloning
Define a global variable, say cloneCount, that stores how many clones have been created, so that we can generate unique IDs
Clone your target <div> element.
Modify all IDs in your target element and its children (use .add() to create a superset) by simply appending cloneCount
Append cloned element to the DOM
If you want to limit the number of clones, simply track cloneCount in the method. When it exceeds a certain threshold, return to exit the function.
Here is a proof-of-concept example:
var $div = $('#clonedDiv');
var cloneCount = 0,
maxCloneCount = 5;
var clone = function() {
// Stop execution if we have cloned max number of times
if (cloneCount >= maxCloneCount)
return;
// Construct clone
var $clone = $div.clone();
// Replace all IDs (of clone and its children) to make sure it is unique
$clone.add($clone.children()).attr('id', function() {
return this.id + '_' + cloneCount;
});
// Append to DOM
$('body').append($clone);
cloneCount++;
};
$('button').on('click', function(e) {
e.preventDefault();
clone();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="clonedDiv">
<p>Clone this div</p>
<input type="file" id="clonedInput">
</div>
<br>
<button type="button">Clone me</button>
To achieve expected result, use below option
var div = document.getElementById('clonedDiv');
var count = 0;
$('button').on('click',function(){
if(count <= 3){
$('#clonedDiv').clone().attr('id','cloneDiv'+count).appendTo('body');
}
count++;
})
https://codepen.io/nagasai/pen/JrjNmq
Restrict the number clones using count variable
Using ID attribute and count, different ids can be assigned to cloned div - cloneDiv
<!DOCTYPE html>
<html>
<head>
<script data-require="rxjs#4.0.6" data-semver="4.0.6" src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.0.6/rx.all.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<div id = 'files'>
</div>
<br>
<button type="button" id = 'clonebtn'>Clone me</button>
</body>
<script type="text/javascript" charset="utf-8">
// Code goes here
window.onload = init;
function init() {
const observable = Rx.Observable.fromEvent($('#clonebtn'),'click');
observable
.take(4)
.map(1)
.scan((acc, curr) => acc + curr)
.subscribe(i=>$('#files').before(getFileTemplate(i)));
}
function getFileTemplate(i){
return `<div id='clonedDiv${i}'>
<p>Clone this div${i}</p>
<input type="file" id='clonedInput${i}'>
</div>`;
}
</script>
</html>

assign selected text to a variable in designMode

I have a piece of code that sets the document to designMode and then operates on pieces of selected text using the document.execCommand() function.
It provides various functionality - for example it allows the user to turn a selected line of text to bold or italic (essentially the functionality of a text editor like this one that I am typing into now).
Here is a simplified example of the code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div>
This is some text - highlight a section of it and press h1 then li
</div>
<button onclick="setToHeader()" id="h1" style="width:100px" unselectable="on">h1</button>
<button onclick="setToList()" id="li" style="width:100px" unselectable="on">li</button>
<script>
document.designMode = 'on';
function setToHeader() {
document.execCommand('formatBlock', false, 'h1');
}
function setToList() {
document.execCommand('insertUnorderedList', false, null);
}
</script>
</body>
</html>
My problem here is that I do not want to be able to use the li button - i.e. convert the selected text to list format, when it is already converted into heading format with the h1 button.
I think I want to be able to read the selected text and simply check it with something like:
// var selectedText = ???
var isHeading = selectedText.search('h1') > -1
Is this the way, or is there a better approach?
How can I get hold of the relevant selected text and assign it to a variable?
You need a little bit more effort. Need to use jquery also, check it out:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<div>This is some text - highlight a section of it and press h1 then li </div>
<div>This is some other text - highlight a section of it and press h1 then li </div>
<button onclick="setToHeader()" id="h1" style="width:100px" unselectable="on">h1</button>
<button onclick="setToList()" id="li" style="width:100px" unselectable="on">li</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script>
document.designMode = 'on';
setInterval(function () {
var el = getSelectionContainerElement();
if($(el).is('h1')){
$("#li").attr("disabled", true);
}
else
{
$("#li").attr("disabled", false);
}
}, 100);
function setToHeader() {
document.execCommand('formatBlock', false, 'h1');
}
function setToList() {
document.execCommand('insertUnorderedList', false, null);
}
function getSelectionContainerElement() {
var range, sel, container;
if (document.selection && document.selection.createRange) {
// IE case
range = document.selection.createRange();
return range.parentElement();
} else if (window.getSelection) {
sel = window.getSelection();
if (sel.getRangeAt) {
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
} else {
// Old WebKit selection object has no getRangeAt, so
// create a range from other selection properties
range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
// Handle the case when the selection was selected backwards (from the end to the start in the document)
if (range.collapsed !== sel.isCollapsed) {
range.setStart(sel.focusNode, sel.focusOffset);
range.setEnd(sel.anchorNode, sel.anchorOffset);
}
}
if (range) {
container = range.commonAncestorContainer;
// Check if the container is a text node and return its parent if so
return container.nodeType === 3 ? container.parentNode : container;
}
}
}
</script>
</body>
</html>
You can get hold of the selected text using the selection object.
e.g. in IE11:
getSelection()
Full documentation can be found here:
https://msdn.microsoft.com/en-us/library/ms535869(v=vs.85).aspx

Appending a div element using javascript

I am new to jQuery and I am practicing appending div elements. Here is my code:
<!doctype html>
<html lang="en">
<head>
<title>Div Id</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
</head>
<script>
document.body.onload = addElement;
function addElement () {
// create a new div element
// and give it some content
var newDiv = document.createElement("div");
var newContent = document.createTextNode("This is a first division");
newDiv.appendChild(newContent); //add the text node to the newly created div.
// add the newly created element and its content into the DOM
var currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
</script>
<body>
<div id="div1">This is a second division.</div>
</body>
</html>
Output would be:
This is a second division.
But as per my implementation the output should be
This is a first division
This is a second division.
I am not able to figure where it is going wrong. Please someone help me out with this.
Thanks in advance.
onload is a property of the window object, not the document.body object.
This works:
window.onload = addElement;
actually it works.... try to move your javascript in the head of you html file
document.body.onload = addElement;
function addElement () {
// create a new div element
// and give it some content
var newDiv = document.createElement("div");
var newContent = document.createTextNode("This is a first division");
newDiv.appendChild(newContent); //add the text node to the newly created div.
// add the newly created element and its content into the DOM
var currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
<div id="div1">This is a second division.</div>
$('#div1').before('<div id=div0>This is a first division</div>')
demo
If you want Jquery it is as simple as this.
If you want to use jQuery you can check this fiddle:
$('body').prepend('<div>This is a first division</div>');

Firefox select text range

A quick question: how do I programatically select the text fragment of the page in FireFox? For example, there's a paragraph of text, user clicks the button and symbols from 10-th to 15-th are selected as if user dragged a mouse in a regular way.
In Firefox, you can use the Range object, as specified by W3C.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Range test</title>
<style>
#trigger { background: lightgreen }
</style>
</head>
<body>
<p id="test">This is some (rather short) text.</p>
<span id="trigger">→ Click here! ←</span>
<!-- Yes, I know, ‘Click here!’ is an evil message -->
<script>
var testCase = function () {
var userSelection;
if (window.getSelection) { // W3C default
userSelection = window.getSelection();
} // an extra branch would be necessary if you want to support IE
var textNode = document.getElementById('test').firstChild;
var theRange = document.createRange();
// select 10th–15th character (counting starts at 0)
theRange.setStart(textNode, 9);
theRange.setEnd(textNode, 14);
// set user selection
userSelection.addRange(theRange);
};
window.onload = function () {
var el = document.getElementById('trigger');
el.onclick = testCase;
};
</script>
</body>
</html>
Note that you have to get the TextNode to set the selection, which is the firstChild of the <p> element. Also note that this example will not work in IE, you have to use some proprietary methods. A nice introduction is on QuirksMode.
I'm not sure if there's a way to do it for arbitrary DOM elements like paragraphs, but for textarea elements, I believe you need to use the selectionStart and selectionEnd properties and specify where to start and end.
var textarea = document.getElementsByTagName('textarea')[0];
textarea.selectionStart = 10;
textarea.selectionEnd = 15;
Hope this helps!

Categories

Resources