i`m working on a Greasmonkey Script, no i want to know how i can test a class for its content.
For example:
<div class="same">Some content</div>
<div class="same">Other content</div>
<div class="same">evenmorecontent</div>
<div class="same">yesthisisalsocontent</div>
<div class="same">youmaynotizedthat</div>
<div class="same">thecontentisdifferent</div>
<div class="same">the cake is yellow</div>
Now i create a array with this classes
var arr = document.getElementsByClassName('same');
Now i want to get the position of "evenmorecontent" in the Array! How do i do this?
If you wanted to use jQuery, you could do this in one line with the contains selector.
In straght JS you need to loop through the array:
var pos = -1;
for(var x=0;x<arr.length;arr++) {
if(arr[x].innerHTML.indexOf('evenmorecontent')>-1) {
pos = x;
}
}
If it occurs more than once, this will return the last position. It will return -1 if not found.
Try not to mix vanilla JS and jQuery while doing this. The following function returns the position of the first occurrence of the text:
var getTextPosition = function (selector, text) {
var pos = -1;
$(selector).each(function(index) {
if($(this).text() === text) {
pos = index;
return false;
}
});
return pos;
};
Example usage:
getTextPosition(".same", "evenmorecontent");
This isn't possible with just one selector. You'll have to loop:
var sameEl = (function() {
var sameArr = document.getElementsByClassName('same');
for (index in sameArr) {
if (sameArr[index].innerHTML === 'evenmorecontent') {
return sameArr[index];
}
}
})();
Related
So I have a container with a grid of items and I want to be able to detect hover between rows. Not the individual items.
Its important to remember that the number of items, in the container and per row, will change.
My container of items currently looks like this
<div class="container">
<div class="hover-placeholder"></div>
<div class="row">
<!-- Start : Items -->
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
<div class="col-md-3">...</div>
...
<div class="col-md-3">...</div>
<!-- End : Items -->
</div>
</div>
Preferably I DO NOT want to put a placeholder element every 4th item. Mainly because on smaller screens the number of items per row will reduce. This is why in my example above I have a single placeholder outside the grid that I want to transform: translateY(..) to the position between the hovered rows.
This is what I have currently: https://jsfiddle.net/0t8c0h4m/
Its nowhere near the result I am after but I have got to the point where I am overthinking it and getting stuck.
Any help would be great!
UPDATE
The goal for this functionality is when the user hovers the negative space, the .hover-placeholder will translate to that position and become visible. And when clicked will add a permanent separator between the rows.
SUCCESS!
I have solved my issue! Thank you all for your help.
Here is my solution: https://jsfiddle.net/0t8c0h4m/9/
I think that you are complicating stuff too much if you are only looking for the hover effect between the elements and nothing else.
Updated:
var offsets;
function get_offsets() {
offsets = {};
$('.card').each(function() {
var $this = $(this);
var card_offset = $this.offset().top;
offsets[card_offset] = {
ele: $this
};
})
}
get_offsets();
function get_last_ele(mouse_location) {
var element;
var previous_key;
$.each(offsets, function(key, obj) {
if (key > mouse_location && previous_key > 0) {
element = offsets[previous_key].ele;
return false;
}
previous_key = key;
})
return element;
}
$('.container').mousemove(function(e) {
if ($(e.target).parents('.row').length == 0) {
var last_item_row = get_last_ele(e.pageY)
console.log(last_item_row.text())
}
});
JSFiddle: https://jsfiddle.net/0t8c0h4m/6/
I'm only providing the code that gets the last item on the row before the space you are hovering. From there you can append the line or transition the placeholder the way you like it.
Please try with following script
(function($) {
'use strict';
// Count items per row
function items_per_row($collection) {
var count = 0;
$collection.each(function() {
var $this = $(this);
if ($this.prev().length > 0) {
if ($this.position().top !== $this.prev().position().top) {
return false;
} else {
count++;
}
} else {
count++;
}
});
var last = count ? $collection.length % count : 0,
rows = count ? Math.round($collection.length / count) : 0;
if (last == 0) {
last = count;
}
return {
count: count,
last: last,
rows: rows
}
};
// On card hover, print current row
$('.card').mouseenter(function() {
var $card = $(this);
var item_count = items_per_row( $('.card') ),
index = $(this).index(),
current_row = Math.floor(index / item_count.count) + 1;
$('pre').text( $card.find('.inner').text() + ' is in row '+ current_row );
});
$('.card').mouseout(function(){
$('pre').text('');
});
})(jQuery);
So I have come up with a solution to show dividers between each row using CSS. I just have been overthinking this issue.
I have added a dividing element after each item and with css nth-child() I can show specific dividers at each break point.
I have also added the grouping functionality I was aiming for.
Updated example: https://jsfiddle.net/0t8c0h4m/9/
Using javascript, I'd like to get the selected text within a div. My question is almost the same as Get selected text from only one div except that within the div there may be an arbitrary number of other nested divs. The answer to that question only works for one nested div.
In other words, say I have a div, STUFF. STUFF could be arbitrary HTML, including plenty of other nested divs. I want the selected HTML within the forkfork div, including any selected nested divs.
Updated: Here's some code, adapted from the previous question. I want to be able to select anything within the "forkfork" div, and have it returned, ideally the raw HTML.
<html>
<body>
<div id="forkfork">want to be able to select anthing within here
<div id="child">or in here</div>
<div id="child1">perhaps in here,
<div id="child2">maybe in here,
<div id="child3">or in here
</div>
</div>
</div>
though here.
</div>
<div id="b">don't want to have this returned.</div>
<button id="btn">btn
<script>
function select() {
var flag = 0;
sel = window.getSelection();
for (var i = 0; i < sel.rangeCount; i++) {
var s = sel.getRangeAt(i).startContainer.parentNode.id;
var e = sel.getRangeAt(i).endContainer.parentNode.id;
if (s == "forkfork") flag = 1;
if (flag = 1 && e == "forkfork" || e == "child") flag = 2;
}
if (flag == 2) alert(sel);
}
document.getElementById("btn").addEventListener("click", function () {
select();
});
</script>
</body>
And a jsfiddle: http://jsfiddle.net/4n3ud3nb/1/
Using cloneContents() from the range the contents of the selection could be returned.
The first cloneContents call will return the contents if there was an over run of our targeted element forkfork.
If we find no over run of the selection, we will check to make sure our selection starting range is a child of the targeted element.
Here is the example snippet:
function select(event) {
var sel;
//console.log(document.getSelection);
if (document.getSelection)
sel = document.getSelection();
else
sel = document.selection.createRange();
//console.log('sel',sel);
var selText = '';
var forkfork = document.getElementById("forkfork");
if (sel.rangeCount > 0) {
var range = sel.getRangeAt(0);
var clonedSelection = range.cloneContents().querySelector("#forkfork");
if (clonedSelection) {
selText = clonedSelection.innerHTML;
} else {
clonedSelection = range.cloneContents();
var startNode = sel.getRangeAt(0).startContainer.parentNode;
//console.log(isChild(startNode, forkfork));
if (isChild(startNode, forkfork)) {
var div = document.createElement('div');
div.appendChild(clonedSelection);
selText = div.innerHTML;
}
}
}
alert(selText.toString());
}
function isChild(child, parent) {
if (child === parent) return true;
var current = child;
while (current) {
if (current === parent) return true;
current = current.parentNode;
}
return false;
}
document.getElementById("btn").addEventListener("click", select);
#a {
background: #C0D9AF;
}
#b,#b2 {
background: #ECC8EC;
}
#c {
background: #AFEEEE;
}
<div id="b">don't want to have this returned.</div>
<div id="forkfork">want to be able to select anthing within here
<div id="child">or in here</div>
<div id="child1">perhaps in here,
<div id="child2">maybe <strong>in</strong> here,
<div id="child3">or in here</div>
</div>
</div>
though here.
</div>
<div id="b2">And, don't want to have this returned.</div>
<button id="btn">btn</button>
.innerHTML will return everything inside the div.
(function() {
$(document).ready(function() {
var x = document.getElementById('forkfork').innerHTML;
console.log(x);
});
}());
console.log(x) Will output this:
want to be able to select anthing within here
<div id="child">something else</div>
<div id="child1">another thing,
<div id="child2">and another thing,
<div id="child3">and another thing
</div>
</div>
</div>
though here.
If you want all text:
(function() {
$(document).ready(function() {
var x = $('#forkfork').text();
console.log(x);
});
}());
Which will output:
want to be able to select anthing within here
something else
another thing,
and another thing,
and another thing
though here
I want to search for nested divs like these in my DOM
<div class="two-columns some-other-class">
<div class="two-columns some-other-class">
</div>
</div>
I tried to search nested divs like this
$("div[class$='columns'] div[class$='columns']")
but it's not working :(
Use the Attribute Contains Selector:
$("div[class*='columns'] div[class*='columns']")
Edit:
If you want that exact functionality you might want to extend the jQuery selector engine:
$.extend($.expr[':'], {
classEndsWith: function(e, i, meta) {
var found = false
var classes = e.className.toLowerCase().split(' ');
var suffix = meta[3].toLowerCase();
$.each(classes, function(i, c) {
// Check if className ends with value in suffix
if (c.indexOf(suffix, c.length - suffix.length) !== -1) {
found = true;
return false;
}
});
return found;
}
});
var element = $('div:classEndsWith(columns) div:classEndsWith(columns)');
See JSFiddle.
$("div[class$='columns'] div[class$='columns']")
Is working. Check the fiddle
I have a an html code like this
<div>
<p> blah blah </p>
<div class = foo>
//some code
</div>
//some text
</div>
What i want to do by javascript is to add a wrapper div to the inner div with class foo. So that the code becomes something like
<div>
<p> blah blah </p>
<div id = temp>
<div class = foo>
//some code
</div>
</div>
//some text
</div>
Please tell me how to do something like this. Non jquery solutions would be more helpful.. :)
Using POJS is pretty simple:
function divWrapper(el, id) {
var d = document.createElement('div');
d.id = id;
d.appendChild(el);
return d;
}
Make sure you pass it something that can be wrapped in a div (e.g. don't give it a TR or TD or such).
You'll need some helper functions, I'm not going to post a getElementsByClassName function here, there are plenty on the web to choose from, a good one should first try qSA, then DOM method, then custom function.
Assuming you have one, consider something like:
function wrappByClass(className) {
var el, elements = getElementsByClassName(className);
for (var i = elements.length; i--;) {
el = elements[i];
el.parentNode.replaceChild(divWrapper(el, 'foo' + i), el);
}
}
Edit
On reflection, I prefer the following method. It inserts the wrapper div into the DOM first, then moves the element to be wrapped into it. The above seems to move the element out of the DOM, then wants to use its position in the DOM to insert the new node. It might work, but seems prone to error to me. So here's a better solution, tested in Safari:
// Quick implementation of getElementsByClassName, just for prototypeing
function getByClassName(className, root) {
var root = root || document;
var elements = root.getElementsByTagName('*');
var result = [];
var classRe = new RegExp('(^|\\s)' + className + '(\\s|$)');
for (var i=0, iLen=elements.length; i<iLen; i++) {
if (classRe.test(elements[i].className)) {
result.push(elements[i]);
}
}
return result;
}
var divWrapper = (function() {
var div = document.createElement('div');
return function(el, id) {
var oDiv = div.cloneNode(false);
oDiv.id = id;
el.parentNode.insertBefore(oDiv, el);
oDiv.appendChild(el);
}
}());
function wrapByClassName(className) {
var els = getByClassName(className);
var i = els.length;
while (i--) {
divWrapper(els[i], 'foo' + i)
}
}
var wrapper = document.createelement('div');
var myDiv = document.getelementById('myDiv');
wrapper.appendChild(myDiv.cloneNode(true));
myDiv.parentNode.replaceChild(wrapper, myDiv);
$('.foo').wrap('<div id="temp"/>');
See $.wrap()
Note that if there are more elements than 1 wrapped, you got more elements with the ID "temp"
I have many DIVs, let's say 100, split by categories like that :
<div id="div1" class="cat01">text</div>
<div id="div2" class="cat02">text</div>
<div id="div3" class="cat01">text</div>
<div id="div4" class="cat02">text</div>
<div id="div5" class="cat03">text</div>
<div id="div6" class="cat01">text</div>
And I want to filter a specific class
Let's say I click on a button "filter only cat02"
I then have only this on the page :
<div id="div2" class="cat02">text</div>
<div id="div4" class="cat02">text</div>
I do not have to use a class to define the categories, but it seems the appropriate solution...
Thanks you VERY much for your help!
EDIT : much clearer
Here is the file :
<div id="principal">
<div class="abc1 categ1">Text0</div>
<div class="abc5 categ3">Text0</div>
<div class="abc4 categ2">Text1</div>
<div class="abc7 categ1">Text0</div>
<div class="abc2 categ2">Text2</div>
<div class="abc4 categ3">Text0</div>
<div class="abc6 categ1">Text0</div>
<div class="abc7 categ2">Text3</div>
</div>
and I want this :
Text1
Text2
Text3
You can do something like this:
var list = document.getElementsByClassName('cat02');
for (var i = 0; i < list.length; i++) {
// this will remove the node from the page
list[i].parentNode.removeChild(list[i]);
// if you just want to hide it, you can do this:
// list[i].style.display = 'none';
}
Note that getElementsByClassName isn't supported by most browsers -- for those that don't, you may have to use a custom implementation such as the one here.
Update: If all your DIVs are direct children of a single DIV and they each contain only one class, it makes the task much simpler. You can skip getElementsByClassName and instead just iterate over the children:
function showOnly(parent, className) {
className = ' ' + className + ' ';
var e = parent.firstChild;
while (e != null) {
if (e.nodeType == 1) {
if ((' ' + e.className + ' ').indexOf(className) > -1)
e.style.display = 'block';
else
e.style.display = 'none';
}
e = e.nextSibling;
}
}
showOnly(document.getElementById('masterdiv'), 'cat02');
Edit: There were a couple of errors previously that I've fixed now. The indexOf comparison should be > -1 instead of > 0 and also the list of children includes empty text nodes (spaces between tags) that should be ignored, hence the check for e.nodeType == 1.
Seem like jQuery would help a lot here. You could just call $("div[class!='class02']") and get an array of items you want. Then, you could call .addClass('hidden') or whatever you need to do to the other items.
A brute force solution:
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (list[i].className != 'cat02') {
list{i].style.display = 'hidden';
}
}
Wrap this in a function to something like this:
function filterDivs(nameToFilter) {
var list = document.getElementsByTagName('div');
for (var i = 0; i < list.length; i++) {
if (!contains(list[i].className.split(' '), nameToFilter)) {
list{i].style.display = 'hidden';
}
}
}
EDIT:
using this function to search for strings in an array that I found here
function contains(a, obj) {
var i = a.length;
while (i--) {
if (a[i] === obj) {
return true;
}
}
return false;
}
With only 100 divs, it should run pretty quickly, but if you increase this amount a lot, it will become slow.
I also recommend that you don't remove items from a collection while iterating through it, that will cause you trouble. Hide them instead, or work with different collections.