jquery get a href using attr() doesn't work - javascript

I have a page* with several .article divs with the following markup:
<div class="article">
<div class="featured-img">
<a href="https://www.johornow.com/english/things-to-do-at-kota-tinggi/" style="background: none;">
</a>
</div>
<div class="featured-info">
<h3 class="article-title">
Fun Things to Do and Amazing Places to Visit at Kota Tinggi
</h3>
</div>
</div>
Now I want to get all the anchor links:
$('.article').each( e => $(this).find('article-title').find('a').attr("href") )
Surprisingly I failed to do so, I got DOM node. What's wrong with my code?!
*https://www.johornow.com/english/travel/

There are couple of errors in your code:
1) JQuery is all about chaining, that's why functions like each return the initial set and not the elements you return in the function (or a collection of your return values?). What you want/need here is .map.
2) your find was missing the . for addressing a class. Also, you can write it in a single statement find(".article-title a").
3) Fat arrow functions don't establish their own context, thus $(this) won't work. Instead you have to use the "old-school" way of writing functions, to actually have this refer to the single elements.
The following jquery solution
$('.article .article-title a').map( function(){return console.log($(this).attr("href"))} )
creates an array with all the link hrefs.
So does the following vanilla JS (which I actually always prefer):
[].map.call(document.querySelectorAll('.article .article-title a'), e=>e.href )
There you have your fancy fat arrow again - also, it's shorter, (possibly faster) and does not rely on third-party;)

You can get all the links in an array
var m =[];
$('.article').each(function(e,v){
m.push($(this).find('h3 > a').attr("href"))
})
console.log(m)

Related

How to use ">" comparator in AngularJS ng-if statement

Is it possible to use the 'greater than' comparator in an ng-if in HTML?
The problem is that the ">" symbol prematurely closes the HTML tag.
ex.
this: <div ng-if="foo>0" class="bar"> (HTML STUFF) </div>
is read as: <div ng-if="foo"> (0 class="bar"> HTML STUFF) </div>
I ended up getting around this by using ng-if="foo!=0" but I could probably use the less than comparator instead but I was just curious in case I absolutely HAD to use the greater than symbol for some reason. Or would I perhaps have to move this logic somewhere else like in my controller instead of in my view?
EDIT 1
So it definitely seems like the comparator itself isn't the problem and something else is going on in my code. Oddly, when I have spaces before and after the comparator it works but without spaces it doesn't. I'm also using angular 1.3.15 if that means anything.
<div class="paginate" ng-if="list.total > 0"> works
<div class="paginate" ng-if="list.total>0"> does not work
This is an example of using the > symbol. This works fine.
<div ng-if="myvariable.length > 2">
</div>
I recommend creating a method on the scope and abstracting the logic of the condition. The business rules may expand and change. With a separate method you don't need to alter the template.
// in controller
$scope.isValidFoo = function () {
return $scope.foo > 0;
}
// in template
<div ng-if="isValidFoo()">...</div>

Understanding the usage of get() in my sorting script

I recently found a code snippet that I would really like to understand:
var buttons = $('#fruit,#vegetable,#meat').click(function() {
$(this).toggleClass('active');
var classes = buttons.filter('.active').map(function() {
return this.id;
}).get().join(',.');
$('div.fruit,div.vegetable,div.meat').hide().
filter('.' + (classes || 'none')).show();
});
The HTML code :
<div style="float:right; padding:25px;">
<button id="fruit" class="active"><span>fruit</span></button>
<button id="vegetable" class="active">vegetable</button>
<button id="meat" class="active">meat</button>
</div>
<div>
<p>Trying to use buttons as an "or" case rather than "and." When choosing fuit or vegetable, I want to see tomato as part of each list, <em>not just</em> when both are selected.</p>
<div class="fruit">
<p>apple</p>
</div>
<div class="vegetable">
<p>pumpkin</p>
</div>
<div class="vegetable">
<p>okra</p>
</div>
<div class="fruit">
<p>orange</p>
</div>
<div class="meat">
<p>beef</p>
</div>
<div class="fruit vegetable">
<p>tomato</p>
</div>
</div>
The fiddle is here.
I do understand how all the methods work in jQuery like toggleclass, filter and map, I also understand how join works in JS, but in this particular example, I am not able to figure out how get() is working or rather what is it's usage in the script is.
I went through the jQuery documentation for get() and I came across this method for the first time; to me, it seems it's very much similar to eq() in jQuery, but I am still not able to figure out why exactly get is being used in my example.
Can somebody explain this to me ?
.get is used here, because .map returns a jquery style object which contains some functions and information about the contained data. But in this scenario only the values stored within the object (the class names of the active buttons) are wanted. .get is used to get an array containing the raw values and with .join(",.") the values from the array get concatenated to a string. This string then get's used to show all div's that should be active according to the selected buttons.

jQuery highlight pieces of text in an element across tags

I want to select and return searched text using jQuery.
The problem is; parts of the text may be located in <span> or other inline elements, so when searching for 'waffles are tasty' in this text: 'I'm not sure about <i>cabbages</i>, but <b>waffles</b> <span>are</span> <i>tasty</i>, indeed.', you wouldn't get any matches, while the text appears uninterrupted to people.
Let's use this HTML as an example:
<div id="parent">
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
<span>
there's loads of
</span>
tortoises over there, OMG
<div id="child">
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
TURTLES!
</div>
</div>
With this (or similar) JavaScript:
$('div#parent').selectText({query: ['i like', 'turtles', 'loads of tortoises'], caseinsensitive: true}).each(function () {
$(this).css('background-color', '#ffff00');
});
//The (hypothetical) SelectText function would return an array of wrapped elements to chain .each(); on them
You would want to produce this output: (without the comments, obviously)
<div id="parent">
<span style="font-size: 1.2em">
<span class="selected" style="background-color: #ffff00">
I
</span> <!--Wrap in 2 separate selection spans so the original hierarchy is disturbed less (as opposed to wrapping 'I' and 'like' in a single selection span)-->
</span>
<span class="selected" style="background-color: #ffff00">
like
</span>
<span class="selected" style="background-color: #ffff00"> <!--Simple match, because the search query is just the word 'turtles'-->
turtles
</span>
<span>
quite a
</span>
lot, actually.
<span>
there's
<span class="selected" style="background-color: #ffff00">
loads of
</span> <!--Selection span needs to be closed here because of HTML tag order-->
</span>
<span class="selected" style="background-color: #ffff00"> <!--Capture the rest of the found text with a second selection span-->
tortoises
</span>
over there, OMG
<div id="child"> <!--This element's children are not searched because it's not a span-->
<span style="font-size: 1.2em">
I
</span>
like turtles
<span>
quite a
</span>
lot, actually.
TURTLES!
</div>
</div>
The (hypothetical) SelectText function would wrap all selected text in <span class="selected"> tags, regardless of whether parts of the search are located in other inline elements like <span>, '', etc. It does not search the child <div>'s contents because that's not an inline element.
Is there a jQuery plugin that does something like this? (wrap search query in span tags and return them, oblivious to whether parts of the found text may be located in other inline elements?)
If not, how would one go about creating such a function? This function's kinda what I'm looking for, but it doesn't return the array of selected spans and breaks when parts of the found text are nested in other inline elements.
Any help would be greatly appreciated!
Piece of cake! See this.
Folded notation:
$.each(
$(...).findText(...),
function (){
...
}
);
In-line notation:
$(...).findText(...).each(function (){
...
}
);
Three options:
Use the browser's built-in methods for this. For the finding, IE has TextRange with its findText() method; other browsers (with the exception of Opera, last time I checked, which was a long time ago) have window.find(). However, window.find() may be killed off without being replaced at some point, which is not ideal. For the highlighting, you can use document.execCommand().
Use my Rangy library. There's a demo here: http://rangy.googlecode.com/svn/trunk/demos/textrange.html
Build your own code to search text content in the DOM and style it.
The first two options are covered in more detail on this answer:
https://stackoverflow.com/a/5887719/96100
Since I just so happened to be working on a similar thing right now, in case you'd like to see the beginnings of my interpretation of "option 3", I thought I'd share this, with the main feature being that all text nodes are traversed, without altering existing tags. Not tested across any unusual browsers yet, so no warranty whatsoever with this one!
function DOMComb2 (oParent) {
if (oParent.hasChildNodes()) {
for (var oNode = oParent.firstChild; oNode; oNode = oNode.nextSibling) {
if (oNode.nodeType==3 && oNode.nodeValue) { // Add regexp to search the text here
var highlight = document.createElement('span');
highlight.appendChild(oNode.cloneNode(true));
highlight.className = 'selected';
oParent.replaceChild(highlight, oNode);
// Or, the shorter, uglier jQuery hybrid one-liner
// oParent.replaceChild($('<span class="selected">' + oNode.nodeValue + '</span>')[0], oNode);
}
if (oNode.tagName != 'DIV') { // Add any other element you want to avoid
DOMComb2(oNode);
}
}
}
}
Then search through things selectively with jQuery perhaps:
$('aside').each(function(){
DOMComb2($(this)[0]);
});
Of course, if you have asides within your asides, strange things might happen.
(DOMComb function adapted from the Mozilla dev reference site
https://developer.mozilla.org/en-US/docs/Web/API/Node)
I wrote a draft as a fiddle. The main steps:
I made a plugin for jQuery
$.fn.selectText = function(params){
var phrases = params.query,
ignorance = params.ignorecase;
wrapper = $(this);
. . .
return(wrapper);
};
Now I can call the selection as a $(...).selectText({query:["tortoise"], ignorance: true, style: 'selection'});
I know you want to have iterator after the function call, but in your case it is impossible, because iterator have to return valid jQuery selectors. For example:
word <tag>word word</tag> word is not valid selector.
After sanitizing the content of wrapper, for each search makeRegexp() makes personal regular expression.
Each searched piece of html source goes to emulateSelection() and then wrapWords()
Main idea is to wrap in <span class="selection">...</span> each single piece of phrase not separated by tags, but not analyze the whole tree of nodes.
NOTE:
It's NOT working with <b><i>... tags in html. You have to make corrections in regexp string for it.
I not guarantee it will work with unicode. But who knows...
As I understood, we talking about iterators like $.each($(...).searchText("..."),function (str){...});.
Check the David Herbert Lawrence poem:
<div class="poem"><p class="part">I never saw a wild thing<br />
sorry for itself.<br />
A small bird will drop frozen dead from a bough<br />
without ever having felt sorry for itself.<br /></p></div>
Actually, after rendering, browser will understood it like this:
<div class="poem">
<p class="part">
<br>I never saw a wild thing</br>
<br>sorry for itself.</br>
<br>A small bird will drop frozen dead from a bough</br>
<br>without ever having felt sorry for itself.</br>
</p>
</div>
For example, I looking for the phrase: "wild thing sorry for". Therefore, I have to highligt the exerpt:
wild thing</br><br>sorry for
I can not wrap it like this <span>wild thing</br><br>sorry for</span>, then create jQuery selector by some temporary id="search-xxxxxx", and return it back -- it's wrong html. I can wrap each piece of text like this:
<span search="search-xxxxx">wild thing</span></br><br><span search="search-xxxxx">sorry for</span>
Then I have to call some function and return jQuery array of selectors:
return($("[search=search-xxxxx]"));
Now we have two "results": a) "wild thing"; b) "sorry for". Is it really what you want?
OR
You have to write you own each() function like another plugin to jQuery:
$.fn.eachSearch = function(arr, func){
...
};
where arr will be not an array of selectors, but array of arrays of selectors, like:
arr = [
{selector as whole search},
{[{selector as first part of search]}, {[selector as second part of search]}},
...
]

Can't fire click event on the elements with same id

Could you help me to understand - where I made the mistake. I have the following html code:
<div id="container">
Info mail.ru
</div>
<div id="container">
Info mail.com
</div>
<div id="container">
Info mail.net
</div>
and the following js code (using jQuery):
$('#getInfo').click(function(){
alert('test!');
});
example here
"Click" event fired only on first link element. But not on others.
I know that each ID in html page should be used only one time (but CLASS can be used a lot of times) - but it only should (not must) as I know. Is it the root of my problem?
TIA!
upd: Big thx to all for explanation!:)
Use a class for this (and return false in your handler, not inline):
<div id="container">
Info mail.ru
</div>
<div id="container">
Info mail.com
</div>
<div id="container">
Info mail.net
</div>
$('.getInfo').click(function(){
alert('test!');
return false;
});
http://jsfiddle.net/Xde7K/2/
The reason you're having this problem is that elements are retrieved by ID using document.getElementById(), which can only return one element. So you only get one, whichever the browser decides to give you.
While you must, according to the W3 specifications, have only one element with a given id within any document, you can bypass this rule, and the issues arising from the consequences if document.getElementById(), if you're using jQuery, by using:
$('a[id="getInfo"]').click(function() {
alert('test!');
return false;
});
JS Fiddle demo.
But, please, don't. Respect the specs, they make everybody's life easier when they're followed. The above is a possibility, but using html correctly is much, much better for us all. And reduces the impact of any future changes within the browser engines, jQuery or JavaScript itself.
It must only be used once or it will be invalid so use a class instead, return false can also be added to your jQuery code as so: -
$('.getInfo').click(function(){
alert('test!');
return false;
});
<a href="#info-mail.net" **class**="getInfo" ....
First id's are for one element only, you should have same id for several divs.
you can make it class instead.
your example changed:
<div class="container">
<a href="#info-mail.ru" class="getInfo" >Info mail.ru</a>
</div>
<div class="container">
<a href="#info-mail.com" class="getInfo" >Info mail.com</a>
</div>
<div class="container">
<a href="#info-mail.net" class="getInfo" >Info mail.net</a>
</div>
$('.getInfo').click(function(ev){
ev.preventDefault(); //this is for canceling your code : onClick="return false;"
alert('test!');
});
You can use the same id for several element (although the page won't validate), but then you can't use the id to find the elements.
The document.getElementById method only returns a single element for the given id, so if you would want to find the other elements you would have to loop through all elements and check their id.
The Sizzle engine that jQuery uses to find the elements for a selector uses the getElementById method to find the element when given a selector like #getInfo.
I know this is an old question and as everyone suggested, there should not be elements with duplicate IDs. But sometimes it cannot be helped as someone else may have written the HTML code.
For those cases, you can just expand the selector used to force jQuery to use querySelectorAll internally instead of getElementById. Here is a sample code to do so:
$('body #getInfo').click(function(){
alert('test!');
});
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<div id="container">
Info mail.ru
</div>
<div id="container">
Info mail.com
</div>
<div id="container">
Info mail.net
</div>
</body>
However as David Thomas said in his answer
But, please, don't. Respect the specs, they make everybody's life easier when they're followed. The above is a possibility, but using html correctly is much, much better for us all. And reduces the impact of any future changes within the browser engines, jQuery or JavaScript itself.

how to remove tags with JavaScript regular expressions

I have a JavaScript string containing HTML like this:
<div>
<div class="a">
content1
</div>
content 2
<div class="a">
<b>content 3</b>
</div>
</div>
and I want to remove the div's of class="a" but leave their content.
In Python I would use something like:
re.compile('<div class="a">(.*?)</div>', re.DOTALL).sub(r'\1', html)
What is the equivalent using Javascript regular expressions?
Why don't you use proper DOM methods? With a little help from jQuery, that's dead simple:
var contents = $('<div><div class="a">content1</div>content 2<div class="a"><b>content 3</b></div></div>');
contents.find('.a').each(function() {
$(this).replaceWith($(this).html());
});
You can achieve it with regular expressions in JavaScript
var html = '<div> <div class="a"> content1 </div> <div class="a"> content1 </div> ... </div>';
var result = html.replace(/<div class="a">(.*?)<\/div>/g, function(a,s){return s;});
alert(result);
RegExp method replace takes two parameters - first one is the actual re and the second one is the replacement. Since there is not one but unknown number of replacements then a function can be used.
If you want to do this in Javascript, I'm presuming that you are running it in a web browser, and that the 'javascript string' that you refer to was extracted from the DOM in some way.
If both of these case are true, then I'd say that it would be a good idea to use a tried and tested javascript library, such as JQuery (There are others out there, but I don't use them, so can't really comment)
JQuery allows you to do on-the-fly DOM manipulations like you describe, with relative ease...
$('div.a').each(function(){$(this).replaceWith($(this).html());});
JQuery is definitely one of those tools that pays dividends - a failry short learning curve and a whole lot of power.

Categories