I need to be able to select and modify an element in an HTML document. The usual way to find an element using jQuery is by using a selector that selects by attribute, id, class or element type.
However in my case I have the element's HTML DOM and I want to find the element on my document that matches this DOM.
Important :
I know I can use a class selector or ID selector etc.. but sometimes the HTMLs I get don't have a class or an ID or an attribute to select with, So I need to be able to select from the element's HTML.
For example here is the element I need to find :
<span class='hello' data='na'>Element</span>
I tried to use jQuery's Find() but it does not work, here is the jsfiddle of the trial : https://jsfiddle.net/ndn9jtbj/
Trial :
el = jQuery("<span class='hello' data='na'>Element</span>");
jQuery("body").find(el).html("modified element");
The following code does not make any change on the element that is present in my HTML and that corresponds to the DOM I have supplied.
Is there any way to get the desired result either using native Javascript or jQuery?
You could filter it by outerHTML property if you are sure how browser had parsed it:
var $el = jQuery("body *").filter(function(){
return this.outerHTML === '<span class="hello" data="na">Element</span>';
});
$el.html("modified element");
el = jQuery('<i class="fa fa-camera"></i>');
This does not say "find the element that looks like <i class="fa fa-camera"></i>". It means "create a new i element with the two classes fa and fa-camera. It's the signature for creating new elements on the fly.
jQuery selectors look like CSS, not like HTML. To find the i element with those two classes, you need a selector like i.fa.fa-camera.
Furthermore $("document") looks for an HTML element called document. This does not exist. To select the actual document, you need $(document). You could do this:
$(document).find('i.fa.fa-camera').html("modified html")
or, more simply, you could do this:
$('i.fa.fa-camera').html('modified html');
You indicate in a comment to your question that you need to find an element based on a string of HTML that you receive. This is, to put it mildly, difficult, because, essentially, HTML ceases to exist once a browser has parsed it. It gets turned into a DOM structure. It can't just be a string search.
The best you can do is something like this:
var searchEl = jQuery('<i class="fa fa-camera"></i>');
var tagName = searchEl.prop('tagName');
var classes = [].slice.apply(searchEl.prop('classList'));
$(tagName + "." + classes.join('.')).html('modified html');
Note that this will only use the tag name and class names to find the element. If you also want IDs or something else, you'd need to add that along the same lines.
You should use Javascript getting the elements by something like
document.getElementById...
document.getElementsByClassName...
document.getElementsByTagName...
Javascript is returning the elements with the Id, Class or Tag Name you chose.
You can get en element with document.querySelector('.fa-camera')
with querySelector you can select IDs and Classes
You can simply refer to it by its class names.
$('.fa.fa-camera').html("modified html");
Similar to this answer https://stackoverflow.com/a/1041352/409556
Here is a full example:
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script>
$(document).ready(function(){
$('.fa.fa-camera').html("modified html");
});
</script>
</head>
<body>
<i class="fa fa-camera"><h1>Some HTML</h1></i>
</body>
</html>`
The one thing that you could use is to check attributes (class and id goes here too in some way) that element have, and the build jQuery selector or DOM querySelector to find the element you need. The hardest part would be to find element based on innerHTML property - "Element" text inside it, for this one you'll probably have to grab all similar element and then search through them.
<span class='hello' data='na'>Element</span>
jQuery('body').find('span.hello[data=\'na\']').html('modified element')
Take notice of 'span' - that's tag selector, '.hello' - class, '[data="na"]' data attribute with name of data.
Jsfiddle link here that extends your example;
Related
I am writing a small library where I am in need of selecting a relative element to the targeted element through querySelector method.
For example:
HTML
<div class="target"></div>
<div class="relative"></div>
<!-- querySelector will select only this .target element -->
<div class="target"></div>
<div class="relative"></div>
<div class="target"></div>
<div class="relative"></div>
JavaScript
var target = document.querySelectorAll('.target')[1];
// Something like this which doesn't work actually
var relativeElement = target.querySelector('this + .relative');
In the above example, I am trying to select the .relative class element relative only to the .target element whose value is stored in target variable. No styles should apply to the other .relative class elements.
PS: the selectors can vary. So, I can't use JavaScript's predefined methods like previousElementSibling or nextElementSibling.
I don't need solution in jQuery or other JavaScript libraries.
Well it should be ideally:
var relativeElement = target.querySelector('.relative');
But this will actually try to select something inside the target element.
therefore this would only work if your html structure is something like:
<div class="target">
<div class="relative"></div>
</div>
Your best bet would probably in this case be to use nextElementSibling which I understand is difficult for you to use.
You cannot.
If you insist on using the querySelector of the subject element, the answers is there is no way.
The spec and MDN both says clearly that Element.querySelector must return "a descendant of the element on which it is invoked", and the object element you want does not meet this limitation.
You must go up and use other elements, e.g. document.querySelector, if you want to break out.
You can always override Element.prototype.querySelector to do your biddings, including implementing your own CSS engine that select whatever element you want in whatever syntax you want.
I didn't mention this because you will be breaking the assumption of a very important function, easily breaking other libraries and even normal code, or at best slowing them down.
target.querySelector('.relative');
By using querySelector on the target instead of document, you scope the DOM traversal to the target element.
It is not entirely clear from your explanation, but by related i assume you mean descendant?
To get all target elements you can use
document.querySelectorAll('.target')
And then iterate the result
I found a way which will work for my library.
I will replace "this " in the querySelector with a unique custom attribute value. Something like this:
Element.prototype.customQuerySelector = function(selector){
// Adding a custom attribute to refer for selector
this.setAttribute('data-unique-id', '1');
// Replace "this " string with custom attribute's value
// You can also add a unique class name instead of adding custom attribute
selector = selector.replace("this ", '[data-unique-id="1"] ');
// Get the relative element
var relativeElement = document.querySelector(selector);
// After getting the relative element, the added custom attribute is useless
// So, remove it
this.removeAttribute('data-unique-id');
// return the fetched element
return relativeElement;
}
var element = document.querySelectorAll('.target')[1];
var targetElement = element.customQuerySelector('this + .relative');
// Now, do anything with the fetched relative element
targetElement.style.color = "red";
Working Fiddle
what's the different between using:
// assuming using elements/tags 'span' creates an array and want to access its first node
1) var arrayAccess = document.getElementsByTagName('elementName')[0]; // also tried property items()
vs
// assuming I assign an id value to the first span element/tag
// specifically calling a node by using it's id value
2) var idAccess = document.getElementById('idValue');
then if I want to change the text node....when using example 1) it will not work, for example:
arrayAccess.firstChild.nodeValue = 'some text';
or
arrayAccess.innerText/innerHTML/textContent = 'some text';
If I "access" the node through its id value then it seems to work fine....
Why is it that when using array it does not work? I'm new to javascript and the book I'm reading does not provide an answer.
Both are working,
In your first case you need to pass the tag name instead of the element name. Then only it will work.
There might be a case that you trying to set input/form elements using innerHTML. At that moment you need to use .value instead of innerHTML.
InnerHTML should be used for div, span, td and similar elements.
So your html markup example:
<div class="test">test</div>
<div class="test">test1</div>
<span id="test">test2</span>
<button id="abc" onclick="renderEle();">Change Text</button>
Your JS code:
function renderEle() {
var arrayAccess = document.getElementsByTagName('div')[0];
arrayAccess.innerHTML = "changed Text";
var idEle = document.getElementById('test');
idEle.innerHTML = "changed this one as well";
}
Working Fiddle
When you use document.getElementsByTagName('p'), the browser traverses the rendered DOM tree and returns a node list (array) of all elements that have the matching tag.
When you use document.getElementById('something'), the browser traverses the rendered DOM tree and returns a single node matching the ID if it exists (since html ID's are unique).
There are many differences when to use which, but one main factor will be speed (getElementById is much faster since you're only searching for 1 item).
To address your other question, you already have specified that you want the first element in the returned nodeList (index [0]) in your function call:
var arrayAccess = document.getElementsByTagName('elementName')[0];
Therefore, arrayAccess is already set to the first element in the returned query. You should be able to access the text by the following. The same code should work if you used document.getElementById to get the DOM element:
console.log(arrayAccess.textContent);
Here's a fiddle with an example:
http://jsfiddle.net/qoe30w2w/
Hope this helps!
My code looks like this, in closeup:
<h2>
<span class="stuff">[<a id="someid">stuff</a>]</span> <span class="moreStuff">Another test</span>
</h2>
I've found a way to select my a element, and attach an id to it. What I need to do now is select its parent <h2> element, but not the <span> element. How can I do that (JQuery allowed)?
Edit: when I retrieve the selected <a>s, I get an array of them (there's lots of these structures on my page). When I try to write myArray[someIndex].closest("h2"), it says that the element does not have a closest() method. How would I go about this?
One ways is to use the .parents() method of jQuery, with a selector. Something like this.
$("#someid").parents("h2");
Update:
You can use the .closest() method with a selector, to only get the closest parent that match the selector.
$("#someid").closest("h2");
Update 2:
It would be a bit more work to do it with plain JavaScript. Not sure if it is the most efficient, but one way would be to select the element with document.getElementById() and then get a reference to its parent through the parentNode property. Then you would have to check if it is an h2 element, and if not, look at that elements parent node, and so on.
You could check the jQuery source and see how they have implemented the closest method.
I just needed the same thing. here a vanilla javascript variant:
function findParent(startElement, tagName) {
let currentElm = startElement;
while (currentElm != document.body) {
if (currentElm.tagName.toLowerCase() == tagName.toLowerCase()) { return currentElm; }
currentElm = currentElm.parentElement;
}
return false;
}
The <h2> is not the parent of the <a> but it is an ancestor, use .closest() to select it
$("#someid").closest("h2");
try use .parent() for get exactly double or more level up the DOM tree.
$("#someid").parent().parent();
This is heavily trimmed down source code from a webpage I'm working on right now.
<!--// GRID ENTRY //-->
<li class="entry" id="sjDDulC8wt">
<div class="entry_actions">
<ul class="entry_actions">
<li class='have_it'>
<a href='javascript: haveItem("name", "id", "none")' target='_self' title='Have It' class='have_it'>%</a></li>
</ul>
</div>
</li>
Inside the haveItem() function I'm trying to change the class of the <a> element from 'have_it' to 'have_it selected' to change the appearance of the element. The reason for the id is because I have dozens of these elements on the page. The javascript I'm currently using is:
var targetA = document.getElementbyID(sjDDulC8wt).getElementsbyTitle("Have_it");
targetA.removeClass("have_it").addClass("have_it selected");
When I click the link, the haveItem() function runs, but it doesn't change the class. How can I change my script so that clicking the link will change the class?
I am assuming you are using jQuery since you are using removeClass() and addClass. Otherwise, I would recommend you link to jQuery so that the code below works, or stick with only JavaScript.
var targetA = $('#sjDDulC8wt .have_it');
targetA.addClass('selected');
For future reference, here are some things about your code that you can improve:
getElementById() accepts the id of the element you want to retrieve as a string. Basically, you should wrap your ID in ' or "
Be careful where you are putting spaces and underscores. They are not the same thing. Your list item has the title Have It, while your JavaScript has Have_it.
Capitalization matters. Have_It is not the same thing as Have_it. Be careful with this when you try to get elements by ID.
A class with spaces in it is actually multiple classes. have_it selected actually has both the have_it class and the selected class. Therefore it not necessary to remove have_it and then add have_it selected - you can go straight to adding the selected class.
The function getElementbyTitle() does not currently exist in JavaScript. Also, be careful again about capitalization. Typically, the first letter of every word in a function is capitalized. Thus, if it did exist it would be called getElementByTitle() (notice the B instead of b).
Here:
var anchor = document.querySelector( '#sjDDulC8wt [title="Have It"]' );
if ( anchor ) anchor.classList.add( 'selected' );
Live demo: http://jsfiddle.net/UZfnh/
(Won't work in older browsers.)
This should do the task for you:
<a title="Have It" class="have_it" onclick="this.className+=' selected'">%</a>
However, you should learn about the DOM. None of the methods you used (getElementbyID, getElementsbyTitle, removeClass and addClass) exists.
When you successfully find the element, you can do one of these:
element.classList.add("selected");
It uses a fairly new classList feature available for FF 3.6, IE 10, Chrome 8, Opera 11.50, Safari 5.1 and above. It the the most preferred way to do it once you are sure your clients will have one of those browsers (near future).
element.className += " selected";
className doc. This works and is probably the easiest way. Not so nice as the classList thing, but available for all browsers.
If you can't use jQuery then you can change the classname of an element using className:
var element = document.getElementById("elementId");
element.className = "have_it selected";
Note also this function: getElementsByClassName (HTML5).
As has been pointed out, you have some other issues selecting your element. From your example, it looks like you want to use:
var element = document.getElementById("sjDDulC8wt");
var children = element.getElementsByClassName("have_it");
children[0].className = "have_it selected";
Note that there's no error handling/null checks here. And this handles a simple case of only changing the first child with the specified class, assuming that it exists.
But prefer jQuery if you can use that. From your use of addClass/removeClass it appears that you already expect jQuery.
I'm trying to do something similar to this question, but it's a bit different, so the solution there isn't working for me.
<span class="a-class another-class test-top-left"></span>
I have an element (this code shows a span but it could be div span or anything). This element has a class beginning with test- (test-top-left, test-top-right etc.) I've triggered a click event on classes starting with test- and saved the clicked object as var object = this;. Simple stuff so far.
What I'm trying to do now is get the full name of that class (test-top-left). I know it starts with test- but what's the full name. The thing is that there are other classes a-class another-class and test-top-left. Can hasClass be used to get the full name of the class? I'd prefer not to use find() or filter() just because there may be additional elements within that also have class="test-"
Edit:
The code I have now is, but it gives me ALL the classes. What I need is the single class beginning with test-.
var object = this;
$(object).attr('class');
So now I for loop through all the classes and test each one separately, which seems like a lot of unnecessary code. I'm hoping jQuery has a clever way to get the exact class that was clicked right away.
Description
You can use jQuerys Attribute Contains Selector, .attr() and .click() method.
Attribute Contains Selector - Selects elements that have the specified attribute with a value containing the a given substring.
.attr() - Get the value of an attribute for the first element in the set of matched elements.
.click() - Bind an event handler to the "click" JavaScript event, or trigger that event on an element.
Sample
html
<span class="anyclass test-hello">Hello World</span>
jQuery
$("[class*='test']").click(function() {
var object = $(this);
alert(object.attr("class").match(/(test-.*?)(?:\s+|$)/)[1])
;});
Check out the updated jsFiddle
Update
If you dont want to use regex you can do this.
$("[class*='test']").click(function() {
var object = $(this);
alert("test-" + object.attr("class").split("test-")[1].split("-"))
;});
More Information
jQuery - Attribute Contains Selector
jQuery - .attr()
jQuery - .click()
jsFiddle Demonstration
This should work for you:
var object = this;
var className = object.className.match(/(test-.*?)(?:\s+|$)/)[1];
Class name is the name of the class you are looking for.
If you don't want to use split or regex, you can try having the class in a separate attribute
<span class="someclass test-something" _rel="test-something">test<span>
or
<span class="someclass" _rel="test-something">test<span>
with the script
$("[_rel*='test-']").click(....
And to retrieve the attribute, use $(this).attr("_rel")