I have a page that might one of the following:
<span id='size'>33</span>
Or
<span id='size'>
<b>33</b>
<strike>32</strike>
</span>
I would like to grab the value '33' on both cases, is there a CSS selector I can use?
I tried to use the following, #size with no b sibling or b which is a #size sibling:
document.querySelector('#size:not(>b), #size>b').innerText
But I keep getting an error- "Error: SYNTAX_ERR: DOM Exception 12"
According to w3 Spec only Simple Selectors are supported, the thing is that "greater-than sign" (U+003E, >)" is considered as part of the Simple Selectors definition.
You can't do it with a regular CSS selector, but you can do it in a few lines of JS:
var element = document.querySelector('#size');
var b = element.querySelector('b');
var text = b ? b.innerText : element.childNodes[0].nodeValue;
console.log(text);
So really you want significant text (ie other than whitespace, because in your second example there's probably tabs and returns between the span start tag and the b) of #size, or, if that doesn't exist, the significant text of its first element:
// Is text just whitespace?
function isWhitespace(text){
return text.replace(/\s+/,'').length === 0;
}
// Get the immediate text (ie not that of children) of element
function getImmediateText(element){
var text = '';
// Text and elements are all DOM nodes. We can grab the lot of immediate descendants and cycle through them.
for(var i = 0, l = element.childNodes.length, node; i < l, node = element.childNodes[i]; ++i){
// nodeType 3 is text
if(node.nodeType === 3){
text += node.nodeValue;
}
}
return text;
}
function getFirstTextNode(element){
var text = getImmediateText(element);
// If the text is empty, and there are children, try to get the first child's text (recursively)
if(isWhitespace(text) && element.children.length){
return getFirstTextNode(element.children[0])
}
// ...But if we've got no children at all, then we'll just return whatever we have.
else {
return text;
}
}
The day we'll have CSS Level 4 selectors and the parent selector you'll be able to use a simple selector but for now you can't do it directly.
You could iterate to find the first text node but here's a hacky solution :
var text = document.getElementById('size').innerHTML.split(/<.*?>/)[0];
To be used only if you have some idea of the content of your #size element.
Related
I can query for elements for example that have a title property like;
$$('*[title]')
Lets say I want to find all elements with border / or border-radius applied, I tried some queries but they did not work.
$$('*[border-width]')
// returns nothing
$$('*[border-width="1px"]')
// returns nothings
this are applied on class level but I tried a few with inline styling, still does not work.
So how can you find elements with lets say, some specific border, padding, etc applied?
Working Demo: https://dojo.telerik.com/IlOVEsAS/7
function getElementByCSSProps(props){
var elements = $("*").filter(function(e){
var borderWidth = parseInt($(this).css(props));
return borderWidth > 0;
});
return elements;
}
The above function will return elements based on the passed parameter.
For e.g
getElementByCSSProps("border-width"); - This line will return all elements with border.
getElementByCSSProps("border-radius"); - This line will return all elements with border-radius.
I'd like to find and replace specific text strings within an element across multiple URLS.
The element:
<div class="text-row"><p></p></div>
Within the p tags the text strings would be:
1."Endo" change to "Endodontist"
2."MCD" change to "Lead Dentist"
How would I accomplish that using Google Tag Manager?
Using Javascript you can look for the elements that match by class/id and just edit them.
if(document.getElementsByClassName('text-row')){
var elems = document.getElementsByClassName('text-row');
for(var k = 0;k < elems.length;k++){
var text = elems[k].innerText;
if(text.indexOf('Endo') != -1){
text.replace('Endo','Endodontist');
elems[k].innerText = text;
}
}
}
I'm creating a grid of elements and have a hover effect in place, using CSS transitions, for each element. I'd like to add secondary effects on adjacent x- and y-axis elements as well, creating a cloud effect. I imagine I'll be referencing those elements using jQuery's next() and prev() methods, or by $index and $parent.$index.
The grid area will be large enough to prevent row-wrapping (using negative margins and hidden overflow).
Here's a simplified example of my repeat:
<div class="activity-thumb-row" ng-repeat="i in getNumArray(20) track by $index">
<div class="activity-thumb"
ng-class="{'adjacent': adjacent}"
ng-repeat="j in getNumArray(30) track by $index"
ng-mouseenter="highlightActivities()">
</div>
</div>
And a function in the controller (which I realize may not be the best approach):
$scope.highlightActivities = function() {
$(this).next().adjacent = true;
$(this).prev().adjacent = true;
}
How can I target elements adjacent to the hovered element using ng-class (or something else) inside ng-repeat?
Here's a fiddle for fiddling.
For reference, here are some related discussions:
Change class on mouseover in directive
Angular js ng repeat with conditional ng class not applying css class
ng-mouseover and leave to toggle item using mouse in angularjs
Here's a directive that calculates all of the indices of adjacent cells and adds the adjacent class using jQuery only ... not ng-class.
Assumes that rows will wrap , would need adjusting for individual row elements
.directive('activityThumb', function() {
return {
restrict: 'C',
link: function(scope, elem) {
elem.bind('mouseenter', function(e) {
var elW = elem.width(),
$parent =elem.parent(),
parentW = $parent.width(),
$items = $parent.children(),
numItems =$items.length
itemsPerRow = Math.floor(parentW / elW),
idx = elem.index(),
rowIndex = idx % itemsPerRow;
/* object of various indices , easy to inspect*/
var adjacentIdx = {
top: idx > itemsPerRow ? idx - itemsPerRow : false,
right: rowIndex != itemsPerRow ? idx + 1 : false,
left: rowIndex > 0 ? idx - 1 : false,
bottom: (numItems - idx) > itemsPerRow ? idx + itemsPerRow : false
}
console.dir(adjacentIdx);
$items.removeClass('adjacent')
$.each(adjacentIdx, function(position, index){
if(index !== false){
$items.eq(index).addClass('adjacent');
}
});
});
}
}
});
It wouldn't take much tweaking to remove jQuery dependency either.
Also would need additional directive on parent to remove extra classes when mouse leaves the main parent from one of the edges
DEMO
First, it's not a good idea to deal with DOM elements in the controller.
Also, this problem seems to be mostly styling related, and not functionality related. I would thus try to keep the logic in the View and not in the controller.
There are 2 ways to deal with View-specific logic: 1) using custom directives or 2) View-defined scope variables
The second approach can work here and seems like the cheapest approach, but also a bit ugly. It ng-inits the rowHighlight array in the scope and sets which element is highlighted:
<div ng-repeat="i in getNumArray(20) track by $index" ng-init="rowHighlight = []">
<div class="activity-thumb"
ng-repeat="j in getNumArray(30) track by $index"
ng-class="{'adjacent': rowHighlight[$index-1] || rowHighlight[$index+1]}"
ng-mouseenter="rowHighlight[$index] = true"
ng-mouseleave="rowHighlight[$index] = false">
</div>
</div>
updated fiddle
I am traversing a DOM using Qt's WebKit classes. Please have a look on the following pseudo HTML:
<br>111<a class="node">AAA</a>
<br>222<a class="node">BBB</a>
...
I can easily find the anchors using findAll(). However I also need to get the text before the elements ("111" and "222"). I tried to use previousSibling() but of course that gives me the <br> element since the "111" and "222" texts are no elements.
I found a function to access text within an element, but how can I access between the <br> and the <a> elements?
It seems it is not possible. The only workaround I could find is getting the plain text of the parent node and parsing the resulting plain text.
This is the way I solved it:
QWebElement *element = ...
// find out if QWebElement has text
QDomDocument doc;
doc.setContent(element->toOuterXml());
QDomElement domelem = doc.documentElement();
for(QDomNode n = domelem.firstChild(); !n.isNull(); n = n.nextSibling())
{
QDomText t = n.toText();
if (!t.isNull())
{
// it has text !
qDebug() << t.data();
break;
}
}
I use Google Chrome developer tools and I am constantly inspecting one element against another back and forth to find out what may be causing a particular styling issue.
It would be nice to compare the differences in style between element 1 and element 2.
Can this be done with chrome currently or via some workaround? Is there a tool out there that can do what I am looking for?
Current example of style difference is that I have an inline H4 next to a A where the A is appearing higher in vertical alignment. I am not seeking a solution in this question as I will sort it out.
Update: As a result of this discussion, the "CSS Diff" Chrome extension was created.
Great question and cool idea for extension!
Proof of concept
As a proof of concept, we can do a small trick here and avoid creating extension. Chrome keeps elements you select via 'inspect element' button in variables. Last selected element in $0, one before that in $1 etc. Basing on this, I've created a small snippet that compares last two inspected elements:
(function(a,b){
var aComputed = getComputedStyle(a);
var bComputed = getComputedStyle(b);
console.log('------------------------------------------');
console.log('You are comparing: ');
console.log('A:', a);
console.log('B:', b);
console.log('------------------------------------------');
for(var aname in aComputed) {
var avalue = aComputed[aname];
var bvalue = bComputed[aname];
if( aname === 'length' || aname === 'cssText' || typeof avalue === "function" ) {
continue;
}
if( avalue !== bvalue ) {
console.warn('Attribute ' + aname + ' differs: ');
console.log('A:', avalue);
console.log('B:', bvalue);
}
}
console.log('------------------------------------------');
})($0,$1);
How to use it?
Inspect two elements you want to compare, one after another, and paste the code above to console.
Result