window.getSelection() doesn't give html nodes - javascript

This is my page source :
<body>
<div>
<p><span id="1">word</span> <span id="2">word</span> </p>
<div><span id="3">word</span> <span id="4">word</span></div>
<ul>
<li><span id="5">word</span> <span id="6">word</span></li>
</ul>
</ul>
<p><span id="7">word</span> <span id="8">word</span> <strong><span id="9">word</span></strong> </p>
</div>
</body>
I want to highlight(apply a new class) user selected spans and get it id's.
I can get user selected content through window.getSelection().But i don't know how to get selected text nodes.
Thanks in advance,
Logan

The window.getSelection() returns a selection object (ref) that includes the start (anchorNode) and end (extentNode). So based on the HTML you provided - with the ID's modified to not using only numbers (ref) - here is a demo. Click on any word, or select a group of words to see them get the "red" class name.
Modified HTML
<div>
<p><span id="s1">word1</span> <span id="s2">word2</span> </p>
<div><span id="s3">word3</span> <span id="s4">word4</span></div>
<ul>
<li><span id="s5">word5</span> <span id="s6">word6</span></li>
</ul>
<p><span id="s7">word7</span> <span id="s8">word8</span> <strong><span id="s9">word9</span></strong> </p>
</div>
Script
$('div').bind('mouseup', function(){
var i,
s = window.getSelection(),
// get ID of starting node
start = parseInt((s.anchorNode.parentNode.id || '').substring(1), 10),
// get ID of end node
end = parseInt((s.extentNode.parentNode.id || '').substring(1), 10),
// start gathering spans
spans = $('#s' + start);
// remove selected class
$('span[id^=s]').removeClass('red');
// add each span
for (i = start; i <= end; i++) {
spans = spans.add( $('#s' + i) );
}
// add selected class
spans.addClass('red');
});
​
​

Related

How to prevent replacing HTML elements when using innerHTML?

I'm matching HTML elements based on their textContent. Then surronding that match with <strong> tags:
const element = [...document.querySelectorAll('a')]
.find(element => element.textContent.match('b'))
element.innerHTML = element.innerHTML.replace('b', '<strong>$&</strong>')
<a href="#">
<br>
<h3>blog</h3>
</a>
There's a problem, though. The code also matches HTML elements. So I get this:
<a href="#">
<<strong>b</strong>r>
<h3>blog</h3>
</a>
Instead of the desired result:
<a href="#">
<br>
<h3>blog</h3>
</a>
How to change my code so it doesn't match HTML elements? Only the text inside them?
Iterate over the children elements of your anchors and reset the HTML based on whether the textContent of the element contains "b".
Note: find will only find the first instance of the thing you're looking for. You need to explicitly iterate over all of the things.
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
const elements = [...document.querySelectorAll('a')];
function embolden(elements, str) {
elements.forEach(element => {
[...element.children].forEach(child => {
if (child.textContent.includes('b')) {
child.innerHTML = child.textContent.replace('b', '<strong>b</strong>');
}
});
});
}
embolden(elements, 'b');
<a href="#">
<br>
<p>blog</p>
</a>
<a href="#">
<br>
<p>blog</p>
<p>Peterborough</p>
</a>
Use innerText instead of innerHTML to match. Insert the replaced values in the innerHTML
const element = [...document.querySelectorAll('#example')]
.find(element => element.textContent.match('b'))
element.innerHTML = element.innerText.replace('b', '<strong>$&</strong>')
<div id="example">
<div>This is some text.</div>
<br>
<div> This is part of the body </div>
</div>
You can use this regex: /(?!<[^>]+)b(?![^<]+>)/g
const element = [...document.querySelectorAll('a')]
.find(element => element.textContent.match('b'))
const string = "b";
const reg = new RegExp("(?!<[^>]+)" + string + "(?![^<]+>)", "g");
element.innerHTML = element.innerHTML.replace(reg, '<mark>$&</mark>')
mark
{
background-color: lightgreen;
}
<a href="#">
<br>
<h3 title="attributes are not affected bbbb">blog hover blog</h3>
</a>

Trying to get multiple <span> elements by classname and change their value to true or false

I've just about tried this every possible way, I'm super new at this.
I'm trying to get the element using class name, and then I'm trying to change it's value to true so that I can run a function I made that uses .push and an if/else statement to build a new array based off of the values in the spans (I'll post that function at the bottom)
Any help anyone can provide would be awesome, I've been at this for the last 3 evenings and I'm just stuck and I have to have this solved by tomorrow.. :(
A billion thanks in advance!
JavaScript
// Function Declaration to check the user's character choice.
function userChoiceCheck(uChoice, low) {
for (var j = 0; j < low.length; j++) {
if (uChoice == low[j]) {
var element = document.getElementsByClassName(low[j]);
element.setAttribute = "true";
console.log(element);
console.log("The value of " + low[j] + " should now be true!");
} else {
document.getElementsByClassName(low[j].class).value = "false";
console.log("The value of " + low[j] + " should now be false!");
}
}
}
HTML
<div class="text-center pt-5">
<h1 id="wordGuessArea">
<span class="m" value="false">__ </span>
<span class="o" value="false">__ </span>
<span class="o" value="false">__ </span>
<span class="s" value="false">__ </span>
<span class="e" value="false">__ </span>
</h1>
</div>
function mentioned above:
// Function Declaration to merge censoredWord Array and upper Array into a
new array called displayArr. Depending on the boolean value of the span that
contains the character.
function mergeArr(low, up, wSplit, cWord) {
for (var m = 0; m < wSplit.length; m++) {
var targetCharSpan = document.getElementsByClassName(low[m]);
var charSpanVal = targetCharSpan.value;
if (charSpanVal == true) {
displayArr.push(up[m]);
} else if (charSpanVal == false) {
displayArr.push(cWord[m]);
}
}
}
I assume that you are having trouble on get all element by class,
if so, you need a loop
getElementsByClassName return a array HTMCollection, instead of using element.setAttribute, you should loop through every element inside your element variable
Like this:
for(let i=0;i<element.length;i++)
{
element[i].setAttribute = "true";
}
You can solve your problem this way using jQuery
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="text-center pt-5">
<h1 id="wordGuessArea">
<span class="m" value="false">__ </span>
<span class="o" value="false">__ </span>
<span class="o" value="false">__ </span>
<span class="s" value="false">__ </span>
<span class="e" value="false">__ </span>
</h1>
</div>
<script>
for(var i=0;i<$('#wordGuessArea span').length;i++){
//Applying for all span elements inside wordGuessArea id
$('#wordGuessArea span').html('Bla');
}
</script>
</body>

Using multiple ids get text from element that is visible

I am using angular js with multiple conditions all using the same id. Need to get text just from the condition that is true.
I just found a big bug in an app I'm about to release.
I am using angularjs/ionic vs 1 and use allot of conditions in my html.
These condition produce great results and show what I want to be shown.
However whenever I save the information it fetches the element id, which each condition has the same id, and instead of giving me the text to the visible condition, I get all of them.
I need a way to grab the id text only if it's visible. This used to work, unless this was missed during testing.
Checking the id in controller.
function JqliteIds(id) {
// var myElement = angular.element( document.querySelector( '#some-id' ) );
var p = angular.element(document.getElementById(id)).text().trim();
p = p.replace(/(\r\n|\n|\r)/gm, " ");
p = p.replace(/ +/g, ' ');
return p;
//stepTwoFormula = stepTwoFormula.replace(/(\r\n|\n|\r)/gm, " ");
//stepTwoFormula = stepTwoFormula.replace(/ +/g, ' ');
}
The html form with multiple conditions. The id is... id="stepOneFormula"
<div class="item item-text-wrap">
<div ng-if="level.light">
<div ng-show="level.stepOne.bleachAdditiveType < 0">
<p id="stepOneFormula">
Mix {{config.productTypes[level.stepOne.productType]}} with
<span ng-if="level.stepOne.productType === 1 || level.stepOne.productType === 2">
the manufacturer suggested developer or
</span> {{level.stepOne.peroxideVolume}} volume / {{level.stepOne.peroxidePercentage}} percent peroxide until it is the consistency of mayonnaise.
</p>
</div>
<div ng-show="level.stepOne.bleachAdditiveType > -1">
<p id="stepOneFormula">
Add approximately {{config.partsInches[level.stepOne.bleachInches]}} of
{{config.productTypes[level.stepOne.bleachAdditiveType]}} to {{config.productTypes[level.stepOne.productType]}}. Mix with
<span ng-if="level.stepOne.productType === 1 || level.stepOne.productType === 2">
the manufacturer suggested developer or
</span> {{level.stepOne.peroxideVolume}} volume / {{level.stepOne.peroxidePercentage}} percent peroxide until it is the consistency of mayonnaise.
</p>
</div>
</div>
<div ng-if="!level.light">
<p>
<!--Going Red-->
<div ng-show="level.stepOne.redRootsTonePercentOne > -1">
<div id="stepOneFormula" ng-include="'app/formula-result/service-types/double-process/red-rootsDbl1.html'"></div>
</div>
<!--Mixed Levels of gray-->
<div ng-show="level.stepOne.grayTonePercentFour > -1">
<div id="stepOneFormula" ng-include="'app/formula-result/service-types/double-process/gray-mixedDbl1.html'" data="level.stepOne"></div>
</div>
<!--Regular Levels-->
<div ng-show="level.stepOne.grayTonePercentFour === -1 && level.stepOne.redRootsTonePercentOne === -1">
<div id="stepOneFormula" ng-include="'app/formula-result/service-types/double-process/genericDbl1.html'"></div>
</div>
</p>
</div>
</div>
As soon as I posted this question I started reading on the ng-show, ng-if and changed my ng-shows to ng-if... it worked.
https://docs.angularjs.org/api/ng/directive/ngIf
Don't need to check if element is visible. Unlike ng-show, the ngIf directive removes or recreates the element. So if it isn't created it's not there so the id is irrelevant.

can't get data attribute with jquery

I am trying to retrieve data-cost("#packages") and append it to #form next to Ticket-price. I am not getting any errors in console. I can't seen to understand what went wrong.
JS:
<script src="jquery-1.11.2.js"></script>
<script>
$(document).ready(function()
{
var price=$("#packages").data("cost");
var amount=$("<span>"+price+"</span>");
$("#form").next().next().append(amount);
});
</script>
HTML:
<div id="packages">
<h2><u>Tourism Packages:</u></h2>
<ul>
<li data-name="Southern Travels">Travels Name: Southern Travels</li>
<li data-cost="2000">Cost per person: 2000</li>
<li>Duration: 3 days & 4 nights</li>
</ul>
</div>
<div id="form">
<label>Number of persons </label>
<input id="input"type="text"/ autofocus>
<p id="ticket-price">Ticket Price</p>
</div>
For this to work as it is, you must edit your HTML as following:
<div id="packages" data-name="Southern Travels" data-cost="2000">
<h2><u>Tourism Packages:</u></h2>
<ul>
<li>Travels Name: Southern Travels</li>
<li>Cost per person: 2000</li>
<li>Duration: 3 days & 4 nights</li>
</ul>
</div>
Either that, or access the data properties of the <li> elements instead of the div#packages (i.e #packages ul li instead of #packages)
$(document).ready(function() {
// Targeting 2nd li element inside #packages
var price=$("#packages li").eq(2).data("cost");
// create a span element with text 'price'
var amount=$("<span>"+price+"</span>");
// append as last child of the form
$("#form").append(amount);
});
You need to look for your data attribute by name - and look at the LI's.
You also need to build your html string and append it properly to the #ticket-price
WOKING EXAMPLE (FIDDLE): http://jsfiddle.net/ojLo8ojz/
JS:
$(document).ready(function(){
var price = $("#packages li[data-cost]").attr("data-cost");
var amount = "<span> "+price+"</span>";
$("#ticket-price").append(amount);
});

Sort div by span class

EDIT
I have this working in fiddle http://jsfiddle.net/gandjyar/HM67V/ but can't get it working outside of fiddle... ideas?
I have the following code:
<div id="sort">
<p>Sort Communities By: North | South | East | West | ALL
</p>
</div>
<div class="fp-floorplans">
<div id="fp-link">
Community 1
</div>
<div id="wpcf-field-location" class="wpcf-field-checkboxes wpcf-field-location">
<span class="wpcf-field-name wpcf-field-checkboxes wpcf-field-location-name">Loation:</span>
<span class="wpcf-field-value wpcf-field-checkboxes-value wpcf-field-location-value">South</span>
</div>
<div id="wpcf-field-schools" class="wpcf-field-textfield wpcf-field-schools">
<span class="wpcf-field-name wpcf-field-textfield wpcf-field-schools-name">School(s):</span>
<span class="wpcf-field-value wpcf-field-textfield-value wpcf-field-schools-value">Southwest</span>
</div>
<div id="wpcf-field-price-starting-at" class="wpcf-field-textfield wpcf-field-price-starting-at">
<span class="wpcf-field-name wpcf-field-textfield wpcf-field-price-starting-at-name">Price Starting At:</span>
<span class="wpcf-field-value wpcf-field-textfield-value wpcf-field-price-starting-at-value">$100's</span>
</div>
</div>
<div class="fp-floorplans">
<div id="fp-link">
Community 2
</div>
<div id="wpcf-field-location" class="wpcf-field-checkboxes wpcf-field-location">
<span class="wpcf-field-name wpcf-field-checkboxes wpcf-field-location-name">Loation:</span>
<span class="wpcf-field-value wpcf-field-checkboxes-value wpcf-field-location-value">East</span>
</div>
<div id="wpcf-field-schools" class="wpcf-field-textfield wpcf-field-schools">
<span class="wpcf-field-name wpcf-field-textfield wpcf-field-schools-name">School(s):</span>
<span class="wpcf-field-value wpcf-field-textfield-value wpcf-field-schools-value">Southeast</span>
</div>
<div id="wpcf-field-price-starting-at" class="wpcf-field-textfield wpcf-field-price-starting-at">
<span class="wpcf-field-name wpcf-field-textfield wpcf-field-price-starting-at-name">Price Starting At:</span>
<span class="wpcf-field-value wpcf-field-textfield-value wpcf-field-price-starting-at-value">$100's</span>
</div>
</div>
and I want to be able to sort by North, South, East, West for the 'wpcf-field-location-value' or just ascending for the 'wpcf-field-schools-value'.
I've tried implementing some suggestions that I've found but none have worked. (Jquery - sort DIV's by innerHTML of children and Sorting divs by number inside div tag and jQuery)
First: you can't have same id values in your html. You can add a user attribute by giving it data-id. An id property value must be unique for the whole document.
Since North,South,East,West are not alphabetically ordered you need to replace them with another value.
Using the code provided here (with some edits): Jquery - sort DIV's by innerHTML of children
var direction={north:0,south:1,east:2,west:3}
function sortOn(primary){
primary=primary.toLowerCase();
for(item in direction){
if(direction[item]===0){
break;
}
}
var tmp=direction[primary];
direction[primary]=0;
direction[item]=tmp;
var items = $(".fp-floorplans").sort(function(a, b) {
var vA = $(a).find("wpcf-field-location-value").text().trim().toLowerCase();
var vB = $(b).find("wpcf-field-location-value").text().trim().toLowerCase();
if(directions[vA]===directions[vB]){
//secondary search
vA = $(a).find("wpcf-field-schools-value").text().trim().toLowerCase();
vB = $(b).find("wpcf-field-schools-value").text().trim().toLowerCase();
return (vA < vB) ? -1 : (vA > vB) ? 1 : 0;
}
// primary searh (a and b cannot be the same)
return (vA < vB) ? -1 : 1;
});
}
sortOn("west");
To change the priority of the order (like west first) you can change the directions variable before sorting:
direction={north:3,south:1,east:2,west:0}
Fist you need to see what value currently holds the 0 and then replace it with the new on.

Categories

Resources