Search for divs that contain specified text - javascript

I want to make a search function on my website, where I search for divs (and leave out the divs which didn't meet what I searched for. The div list looks like this:
<body>
<div class='subjects'>
<div id='subject'>soccer</div>
<div id='subject'>dancing</div>
<div id='subject'>soap</div>
</div>
</body>
For instance, when I search for the 's' it doesn't show the div which the dancing inside and when you write 'soa' it shows soap only (not removing divs not matching, just don't show them).
I really have no experience with searching stuff, so all information is welcome.
ps. the tags I added are the languages that are available, if I need an extension: that's no problem.

You can use jQuery to do it, something like this:
HTML:
<div class='subjects'>
<div>soccer</div>
<div>dancing</div>
<div>soap</div>
</div>
<input type="text" id='search' />
jQuery:
$('#search').on('input', function(){
var text = $(this).val();
$('.subjects div').show();
$('.subjects div:not(:contains(' + text + '))').hide();
});
Fiddle

In POJS and only caring about modern browsers (supporting ECMA5 & HTML5, IE10+)
CSS
.hide {
display: none;
}
HTML
<input id="search"></input>
<div class="subjects">
<div class="subject">soccer</div>
<div class="subject">dancing</div>
<div class="subject">soap</div>
</div>
javascript
document.getElementById("search").addEventListener("keyup", function (evt) {
[].forEach.call(document.querySelectorAll(".subjects .subject"), function (subject) {
if (subject.textContent.indexOf(evt.target.value) === -1) {
subject.classList.add("hide");
} else {
subject.classList.remove("hide");
}
});
}, false);
jsfiddle
In POJS and cross-browser required (IE5.5+)
javascript
function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
function classNameToArray(className) {
return className.split(/ +/);
}
function getElementsByClassName(node, className) {
var array = [],
elements = node.getElementsByTagName("*"),
elementsLength = elements.length,
i = 0,
element,
classNames,
classNamesLength,
x;
while (i < elementsLength) {
element = elements[i];
classNames = classNameToArray(element.className);
for (x = 0, classNamesLength = classNames.length; x < classNamesLength; x += 1) {
if (classNames[x] === className) {
array.push(element);
break;
}
}
i += 1;
}
return array;
}
document.getElementById("search").onkeyup = function (evt) {
var e = evt || window.event,
target = e.target || e.srcElement,
subjects = getElementsByClassName(document, "subjects"),
subject = [],
classnames,
classNamesLength,
classIndex,
element,
length,
index,
text;
for (index = 0, length = subjects.length; index < length; index += 1) {
subject = subject.concat(getElementsByClassName(subjects[index], "subject"));
}
for (index = 0, length = subject.length; index < length; index += 1) {
text = "";
element = subject[index];
walkTheDOM(element, function (currentNode) {
if (currentNode.nodeType === 3) {
text += currentNode.nodeValue;
}
});
classNames = classNameToArray(element.className);
for (classIndex = classNames.length - 1; classIndex >= 0; classIndex -= 1) {
if (classNames[classIndex] === "hide") {
classNames.splice(classIndex, 1);
}
}
if (text.indexOf(target.value) === -1) {
classNames.push("hide");
}
element.className = classNames.join(" ");
}
};
jsfiddle
Or in jQuery (IE6+ or IE9+ depends on jQuery version)
javascript
$("#search").keyup(function (evt) {
var subject = $(".subjects .subject");
subject.removeClass("hide");
subject.each(function (index, element) {
var $element = $(element);
if ($element.text().indexOf(evt.target.value) === -1) {
$element.addClass("hide");
}
});
});
jsfiddle
All of these examples use CSS to style the divs, so it is very easy to change your styling, if you don't want to just show/hide but maybe highlight or place a border.

Related

HTML path from the element to the root JS

For example I have HTML like this
<body>
<div>
something.
</div>
<div>
something else
</div>
<div>
<a> something. </a>
<a> ELEMENT </a>
</div>
</body>
Is there a way to get path from the root to the ELEMENT by using JS, something like this:
body[0]/div[2]/a[1]
So, for the ELEMENT there is need to look on the parent node, check if there exist siblings with the same tag and then correctly assign value and do it recursively to root.
So, for the ELEMENT it is the second (a[1]) child of parent root div which is third (div[2]) child of body.
Is there any way this can be done with JS?
One approach is the following:
function findIndexOfLike(node) {
// creating an Array of the filtered children of the node's
// parent element (using Array.prototype.filter() with
// Function.prototype.call() to apply the Array method
// to the Array-like collection):
var children = Array.prototype.filter.call(node.parentNode.children, function(child) {
// keeping only those elements that are of the same
// tagName:
return node.tagName === child.tagName;
});
// Using Array.prototype.indexOf() to find the index of
// the node from the array of children; and returning that:
return children.indexOf(node);
}
function createIndexedPathTo(node) {
// an empty Array to contain the path:
var path = [],
// initialising the 'current' variable, which we'll
// use to move upwards through the document:
current = node;
// while the node contained in the 'current' variable is
// not the <body> element:
while (current.tagName.toLowerCase() !== 'body') {
// we push the lower-cased tagName of the 'current' node,
// along with its index, to the array:
path.push(current.tagName.toLowerCase() + '[' + findIndexOfLike(current) + ']');
// move the 'current' variable to the parentNode of
// the current element (to move 'up'):
current = current.parentNode;
}
// there can be only one <body> element, but since
// you seem to want it listed we add it here:
path.push('body[0]');
// now we reverse the array, and join it together,
// with the '/' character, to form a string, returning
// that formed string:
return path.reverse().join('/');
}
// calling the function, passing it a DOM Node from which to start:
var route = createIndexedPathTo(document.querySelector('a:nth-child(2)'));
// setting the 'data-routeto' attribute of the <body>
// in order to display that route/path in the document
// using CSS generated content:
document.body.dataset.routeto = route;
function findIndexOfLike(node) {
var children = Array.prototype.filter.call(node.parentNode.children, function(child) {
return node.tagName === child.tagName;
});
return children.indexOf(node);
}
function createIndexedPathTo(node) {
var path = [],
current = node;
while (current.tagName.toLowerCase() !== 'body') {
path.push(current.tagName.toLowerCase() + '[' + findIndexOfLike(current) + ']');
current = current.parentNode;
}
path.push('body[0]');
return path.reverse().join('/');
}
var route = createIndexedPathTo(document.querySelector('a:nth-child(2)'));
document.body.dataset.routeto = route;
body::before {
display: block;
content: 'Route to "Example" element: ' attr(data-routeto);
color: #999;
}
<div>something.</div>
<div>something else</div>
<div> <a> something. </a>
<a id="demo"> ELEMENT </a>
</div>
External JS Fiddle demo, for experimentation.
References:
Array.prototype.filter().
Array.prototype.indexOf().
Array.prototype.join().
Array.prototype.push().
Array.prototype.reverse().
document.querySelector().
Function.prototype.call().
Node.parentNode.
Node.tagName.
String.prototype.toLowerCase().
while (...) {...} statement.
This might be what you were looking for. Else I am sorry I missunderstood your question.
<html>
<head>
<script>
//Recursive function to get element path until html from passed element e;
function getPath(e, d){
d = (d || []);
//if (!e || e.tagName === 'BODY'){ //Body is obivous in most cases tho.
if (!e || !e.parentNode){
return d.join('/');
}
else{
//j is needed since <head> is previous sibling to <body> :s
for (var i = 0, n = e, j = 0; n = n.previousElementSibling; i++) if (n.tagName === e.tagName) j++;
//Here we add the element to the current path \o/
d.push(e.tagName.toLowerCase() + '[' + j.toString() + ']');
return getPath(e.parentNode, d);
};
};
</script>
</head>
<body onclick = 'alert(getPath(this));'>
<div>something.</div>
<div>something else</div>
<div>
<a onclick = 'alert(getPath(this));'>something.</a>
<a onclick = 'alert(getPath(this));'>ELEMENT</a>
</div>
</body>
</html>
javascript: (function() {
if ( typeof document.getElementsByTagName === 'function') {
var elemTag = document.getElementsByTagName('input');
for (var i = 0; i < elemTag.length; i++) {
elemTag[i].addEventListener('mouseup', getPath);
}
var elemTag2 = document.getElementsByTagName('a');
for (var j = 0; j < elemTag2.length; j++) {
elemTag2[j].addEventListener('mouseup', getPath);
}
var elemTag3 = document.getElementsByTagName('select');
for (var p = 0; p < elemTag3.length; p++) {
elemTag3[p].addEventListener('mouseup', getPath);
}
var elemTag4 = document.getElementsByTagName('button');
for (var m = 0; m < elemTag4.length; m++) {
elemTag4[m].addEventListener('mouseup', getPath);
}
var elemTag5 = document.getElementsByTagName('img');
for (var l = 0; l < elemTag5.length; l++) {
elemTag5[l].addEventListener('mouseup', getPath);
}
}
function getPath() {
var domPathArr = [],
elm,
entry;
elm = this;
if ( typeof getIndex === "function" && elm) {
entry = elm.tagName.toLowerCase() + "[" + getIndex(elm) + "]";
if (entry) {
domPathArr.push(entry);
for ( elm = this.parentNode; elm; elm = elm.parentNode) {
entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
if (elm) {
entry += "[" + getIndex(elm) + "]" + "/";
}
domPathArr.push(entry);
}
}
}
domPathArr.reverse();
console.log(domPathArr.join(' '));
}
function getIndex(elm) {
var count = 0;
if (elm) {
for (var siblingElm = elm.previousSibling; siblingElm; siblingElm = siblingElm.previousSibling) {
if (siblingElm.nodeName == elm.nodeName)
count++;
}
}
return count;
}
})();

adding a span to all the numbers in the body tag of the website using javascript

I want to add span tag with the specific class to all the numbers in my website using JavaScript. However I got the following:
<script>
var regex = /(\d+)/,
replacement = '<span class="font-arial">$1</span>';
function replaceText(el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
var nodes = temp_div.childNodes;
while (nodes[0]) {
el.parentNode.insertBefore(nodes[0],el);
}
el.parentNode.removeChild(el);
}
} else if (el.nodeType === 1) {
for (var i = 0; i < el.childNodes.length; i++) {
replaceText(el.childNodes[i]);
}
}
}
replaceText(document.body);
</script>
But the problem is as below example:
If the number is: 45 7320272536
It put like this:
<span class="arial">
<span class="arial">45</span>
</span>
<span class="arial">
<span class="arial">7320272536</span>
</span>
I want like this:
<span class="arial">45</span>
<span class="arial">7320272536</span>
It can be simpler. You don't need to loop over child nodes one more time. Instead you can replace all number occurrences at once if you use global match flag g for regexp object:
/(\d+)/g
So after cleaning the code with replaceChild method, your code becomes:
var regex = /(\d+)/g,
replacement = '<span class="font-arial">$1</span>';
function replaceText(el) {
if (el.nodeType === 3) {
if (regex.test(el.data)) {
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
el.parentNode.replaceChild(temp_div, el);
}
} else if (el.nodeType === 1) {
for (var i = 0; i < el.childNodes.length; i++) {
replaceText(el.childNodes[i]);
}
}
}
replaceText(document.body);
Demo: http://jsfiddle.net/a09vac2t/
Here is a working sample.
<!doctype html>
<html>
<head>
<title>replace test</title>
<script>
var regex = /(\d+)/;
replacement = '<span style="border:1px solid red">$1</span>';
function replaceText(el) {
//alert('Testing: ' + el.data);
//alert('Node type: ' + el.nodeType);
if (el.nodeType === 3) {
if (regex.test(el.data)) {
//alert('Matches');
var temp_div = document.createElement('div');
temp_div.innerHTML = el.data.replace(regex, replacement);
//alert('Making it ' + temp_div.innerHTML);
var nodes = temp_div.childNodes;
while (nodes[0]) {
//alert(nodes.length + ": " + nodes[0].data);
el.parentNode.insertBefore(nodes[0],el);
}
el.parentNode.removeChild(el);
}
} else if (el.nodeType === 1) {
//alert('Looping children: ' + el.childNodes.length);
var rootChildrenCopy = toArray(el.childNodes).slice(0);
for (var i = 0; i < rootChildrenCopy.length; i++) {
replaceText(rootChildrenCopy[i]);
}
}
}
function toArray(obj) {
var array = [];
// iterate backwards ensuring that length is an UInt32
for (var i = obj.length >>> 0; i--;) {
array[i] = obj[i];
}
return array;
}
</script>
</head>
<body onload="replaceText(document.getElementById('abc'))">
<p>This is a number</p>
<div id='abc'>Here is a 45 num</div>
</body>
</html>
Element.childNodes will change when you insert nodes, so you need to save a copy of it when looping. Array.prototype.slice is good for this.
In addition, you need to change your regex to replace all occurrences instead of just the first with the global flag.

Return all elements for which any attribute starts with something

I'm aware of [name^="value"] selector but is there a analogous selector (or technique) that queries all attributes starting with the given value?
I'm looking for something like $("[*^='http://www.something.com']")(that does not exist).
It would match all elements which contains at least one attribute with a value that begins with http://www.something.com.
Say:
<img src="http://www.something.com/image.jpg" />
Something
<link rel="stylesheet" href="http://www.something.com/css/style.css" type="text/css">
Attribute name could be anything, not just src and href, even non standard attributes.
Is there a known way to do it?
I've put together some of the ideas from other answers and wrote a custom selector.
Selector
$.expr[':'].hasAttrStartingWithValue = function (obj, index, meta) {
var startsWithAttrValue = false;
var value = meta[3];
for (var i = 0; i < obj.attributes.length; i++) {
var attr = obj.attributes[i];
// attr.value starts with value
if (attr.specified && attr.value.lastIndexOf(value, 0) === 0) {
startsWithAttrValue = true;
break;
}
}
return startsWithAttrValue;
};
It has not been properly tested for cross-browsing and correctness, and I'm sure that it can be further optimized, but it seems to be working well with IE 11, FF 24 and Chrome 32.
Usage
// Logs every occurrence of a value in any attribute of the page
$(":hasAttrStartingWithValue('http://www.something.com')").each(function (i, e) {
console.log(i + " - " + e.outerHTML);
});
// Matches only children of test
$("#test :hasAttrStartingWithValue('http://www.something.com')")
.css('background-color', 'red');
Working Fiddle.
function strstr (haystack, needle, bool) {
var pos = 0;
haystack += '';
pos = haystack.indexOf(needle);
if (pos == -1) {
return false;
} else {
if (bool) {
return haystack.substr(0, pos);
} else {
return haystack.slice(pos);
}
}
}
$( document ).ready(function(){
$('*').each(function() {
$.each(this.attributes, function() {
// this.attributes is not a plain object, but an array
// of attribute nodes, which contain both the name and value
if(this.specified) {
if( strstr(this.value,'http://') )
alert(this.name+'+'+this.value);
}
});
});
});
Alert All attributes And values...
Custom this code...
jsfiddle
If you want to do it for img, a, and link tags, then you could do it like this:
var ref = '"http://www.something.com"';
var elems = [].slice.call(document.querySelectorAll('img[src='+ref+'], a[href='+ref+'], link[href='+ref+']'));
//do something with the elems array
If you want to go the other route...
JS Fiddle of Working Abomination in Vanilla JS
Code that makes me sad (query everything in sight, loops in loops, regex in loops, etc.):
var rx = /(^http:\/\/www.something.com)/;
var loopAgain = function () {
for (var j = 0, leng = attrs.length; j < leng; j++) {
if (rx.test(attrs[j].value)) {
return true;
}
return false;
}
};
var allTheThings = [].slice.call(document.querySelectorAll('*'));
for (var i = 0, len = allTheThings.length; i < len; i++) {
var attrs = allTheThings[i].attributes;
if (loopAgain()) {
console.log(allTheThings[i]);
}
}

What is the parallel of this code without jQuery?

I need to rewrite this code in pure JavaScript, i.e. without jQuery. It gets the content of a div and adds it after the first image in another div.
$(document).ready(function() {
var teksti = $('#inside1').html();
$('<div id="inside1">' + teksti + '</div><div style="clear:both;"></div>').insertAfter("#artikull > p > img:first");
});
If you only need to support modern browsers, it's not too complicated:
// $(document).ready(function() {
document.addEventListener("DOMContentLoaded", function() {
// var teksti = $('#inside1').html();
var teksti = document.getElementById('inside1').innerHTML;
// $('<div id="inside1">' + teksti + '</div><div style="clear:both;"></div>')
// .insertAfter("#artikull > p > img:first");
document
.querySelector('#artikull > p > img')
.insertAdjacentHTML(
'afterend',
'<div id="inside1">' + teksti + '</div><div style="clear:both">'
);
// });
});
The roughly equivalent lines from the original jQuery are in the comments.
I'm a little confused, though; the code you've presented will create an element with the same ID as the original one and ends up with a couple of divs in a p—resulting in a somewhat deformed DOM. Wouldn't you prefer to simply wrap the existing element and move it instead of creating a new one with exactly the same content, and shouldn't you move it somewhere that accepts block-level children?
function adddiv(){
var teksti = document.getElementById('inside1').innerHTML;
var div = document.getElementById('artikull');
var imazh = div.getElementsByTagName("img")[0];
imazh.outerHTML = imazh.outerHTML + teksti;
}
The code below should be compatible in modern browsers and IE from version 9.
document.addEventListener("DOMContentLoaded", function (e) {
var insideHTML = '<div id="inside1">' + window.document.getElementById('inside1').innerHTML + '</div><div style="clear:both;"></div>',
artikullElement = window.document.getElementById('artikull'),
pElements = (function (nodes) {
var results = [],
node;
for (var i = 0, iLen = nodes.length; i < iLen; i++) {
node = nodes[i];
// Get all children P tags
if (node.nodeValue === 1 && node.tagName === "P")
results.push(node);
}
return results;
})(artikullElement.childNodes),
imgElement = (function (nodes) {
var node;
for (var i = 0, iLen = nodes.length; i < iLen; i++) {
// Get the first child tag image found in any of the P tags
if (node.nodeValue === 1 && node.tagName === "IMG")
return node;
}
})(pElements);
if (imgElement) {
if (imgElement.insertAdjacentHTML)
imgElement.insertAdjacentHTML('beforeEnd', insideHTML);
else {
var range = window.document.createRange(),
docFragment = range.createContextualFragment(insideHTML);
imgElement.parentNode.insertBefore(docFragment, imgElement.nextSibling);
}
}
});

How can i find an element using tabindex - Javascript not jquery

I am trying to move focus to the next element after data is entered in one element.
How can i get the next element using tabindex and move focus.
i am using scanner for data entry. so one second delay on key up will tell
data entered or not. As it is scanner no browser tabbing by user .
<tr>
<td class='form' align='center'> <input type='text' tabindex=1 onkeyup='moveNext(this);' id='from' name='elem1' size='5' value=''> </td>
<td class='form' align='center'><input type='text' tabindex=2 onkeyup='moveNext(this);' id='item' name='elem2' size='5' value=''> </td>
<td class='form' align='center' > <input type='text' tabindex=3 id='calc_price' size='10' name='elem3' value=''> </td>
</tr>
I saw some answers. All in jquery . How can i do that in javascript
You'd have to get the tabindex of the current element, add 1 and then search for an element with that tabindex to set the focus to :
function moveNext(elem) {
var tidx = +(elem.getAttribute('tabindex')) +1,
elems = document.getElementsByTagName('input');
for (var i=elems.length; i--;) {
var tidx2 = elems[i].getAttribute('tabindex');
if (tidx2 == tidx) elems[i].focus();
}
}
FIDDLE
How about something like this
function moveNext(tabInput) {
var lastTab = 3;
var curIndex = tabInput.tabIndex;
var tabs = document.getElementsByTagName("input");
if(curIndex >= lastTab) { //if we are on the last tab then go back to the beginning
curIndex = 0;
}
for(var i=0; i < tabs.length; i++) { // loop over the tabs.
if(tabs[i].tabIndex == (curIndex+1)) { // is this our tab?
tabbables[i].focus(); // Focus and leave.
break;
}
}
Here's an implementation I just wrote. It's a bit more sophisticated in that it filters out hidden and disabled inputs, and handles multiple elements with the same tab index. It's tested only in Firefox so far.
Coffee:
getElementsInTabOrder = (form) ->
# Get all focusable form elements
elements = Array.prototype.filter.call form.elements, (element) ->
return false if element.type is 'hidden'
return false if element.disabled
return true
# Get elements grouped by tab index
elementsByTabIndex = []
for element in elements
tabIndex = element.tabIndex
unless elementsByTabIndex[tabIndex]?
elementsByTabIndex[tabIndex] = []
elementsByTabIndex[tabIndex].push element
# Flatten to output array
return [].concat elementsByTabIndex...
getElementByDelta = (element, delta, wrap = true) ->
elements = getElementsInTabOrder element.form
length = elements.length
index = elements.indexOf element
targetIndex = index + delta
# Deal with edge cases
while targetIndex < 0
return null unless wrap
targetIndex += length
while targetIndex >= length - 1
return null unless wrap
targetIndex -= length
return elements[targetIndex]
getNextElement = (element, wrap = true) ->
return getElementByDelta element, 1, wrap
getPreviousElement = (element, wrap = true) ->
return getElementByDelta element, -1, wrap
module.exports = {
getElementsInTabOrder
getElementByDelta
getNextElement
getPreviousElement
}
Javascript:
// Generated by CoffeeScript 1.10.0
var getElementByDelta, getElementsInTabOrder, getNextElement, getPreviousElement;
getElementsInTabOrder = function(form) {
var element, elements, elementsByTabIndex, i, len, ref, tabIndex;
elements = Array.prototype.filter.call(form.elements, function(element) {
if (element.type === 'hidden') {
return false;
}
if (element.disabled) {
return false;
}
return true;
});
elementsByTabIndex = [];
for (i = 0, len = elements.length; i < len; i++) {
element = elements[i];
tabIndex = element.tabIndex;
if (elementsByTabIndex[tabIndex] == null) {
elementsByTabIndex[tabIndex] = [];
}
elementsByTabIndex[tabIndex].push(element);
}
return (ref = []).concat.apply(ref, elementsByTabIndex);
};
getElementByDelta = function(element, delta, wrap) {
var elements, index, length, targetIndex;
if (wrap == null) {
wrap = true;
}
elements = getElementsInTabOrder(element.form);
length = elements.length;
index = elements.indexOf(element);
targetIndex = index + delta;
while (targetIndex < 0) {
if (!wrap) {
return null;
}
targetIndex += length;
}
while (targetIndex >= length - 1) {
if (!wrap) {
return null;
}
targetIndex -= length;
}
return elements[targetIndex];
};
getNextElement = function(element, wrap) {
if (wrap == null) {
wrap = true;
}
return getElementByDelta(element, 1, wrap);
};
getPreviousElement = function(element, wrap) {
if (wrap == null) {
wrap = true;
}
return getElementByDelta(element, -1, wrap);
};
module.exports = {
getElementsInTabOrder: getElementsInTabOrder,
getElementByDelta: getElementByDelta,
getNextElement: getNextElement,
getPreviousElement: getPreviousElement
};
Get the next ID and focus it. If it exists:
let i = document.activeElement.tabIndex;
if(i >= 0){
let iNext = document.querySelectorAll("[tabindex='"+(i+1)+"']");
if(iNext.length > 0){
elNext = iNext[0].getAttribute('id');
document.getElementById(elNext).focus();
}
}
Basically the code above is based on the following example:
document.querySelectorAll("[tabindex='2']")[0].getAttribute('id');

Categories

Resources