So I'm trying to insert rows and columns into a table using the code below:
add_to_table(type) {
switch (type) {
case "row":
let columns = this.$refs.table.rows[0].cells.length;
let row = this.$refs.table.insertRow(-1);
row.height = "20px";
for (let i = 0; i < columns; i++) {
let cell = row.insertCell(-1);
cell.innerHTML = " ";
}
break;
case "column":
for (let row of this.$refs.table.rows) {
let cell = row.insertCell(-1);
cell.innerHTML = " ";
}
break;
}
}
However, this doesn't seem to maintain the css (doesn't add the data-* stuff to it).
I'm currently working around this by using v-for:
<tr v-for="row in rows">
<td v-for="column in columns">
</td>
</tr>
https://codesandbox.io/s/8n728r5wr8
Your created rows and columns are not getting styled because the <style> you declared is scoped.
For the elements to get the scoped style, they must have a data-v-SOMETHING attribute. The elements you create manually, not via Vue, don't have that attribute.
WARNING: Vue is data-driven, the correct, simplest, more predictable
and maintainable way of achieving what you want is mutating a data
attribute and letting Vue's templates react to it accordingly (using
directives like v-for). Your solution is not optimal. You have been warned.
That being said, you have some options:
Declare an additional <style> (non-scoped) tag along the scoped one. The created elements will pick up these styles. Drawback: the styles will be global. Advantage: you don't depend on Vue internals, you don't have to always add the data-v attribute (see below).
Example:
<style scoped>
...
</style>
<style>
/* EXAMPLE OF GLOBAL STYLE ALONGSIDE A SCOPED ONE */
tr, td {
box-shadow:inset 0px 0px 0px 1px orange;
}
</style>
Get a hold of the data-v-SOMETHING attribute. It is available at this.$options._scopeId. Double Warning: the prefix _ means it is internal code. It may change without notice. Your app may be forever stuck with the current Vue 2.x versions. You have been warned again.
So, whenever you create elements, just add the attribute. Example:
// row example
let row = this.$refs.table.insertRow(-1);
row.setAttribute(this.$options._scopeId, ""); // <== this adds the data-v-XYZ attr
...
// cell example
let cell = row.insertCell(-1);
cell.setAttribute(this.$options._scopeId, ""); // <== this adds the data-v-XYZ attr
Here's a CodeSandbox demo containing examples for both alternatives.
Related
I need to have 3 classes as follow:
.class-4, .class-5, .class-6 {
color: pink;
}
And it works perfectly.
Let's say I need the same but for 100:
.class-4, .class-5, .... .class-100 {
color: pink;
}
Is there anything similar to this or any other way to do this which I can use.
.class->3<101 {
color: pink;
}
To get the same result without writing 97 times the class and the comma?
There is nothing in pure CSS which will do this, but you could use JavaScript to create a stylesheet for you which has all that tedious repetition created automatically.
In this snippet you say what the ends of the class ranges are and what styling is to be put in each of the ranges.
If there is a range which you don't want to alter then you still need to include it but make its styles string just an empty string.
The snippet runs through each of the ranges creating the relevant style sheet entries and puts them in a style element in the head element of the document.
A few fairly random divs are shown here just to test that we are hitting the right ranges.
const rangeEnds = [4, 20, 35, 41, 48, 100];
const styles = ['color: pink;', 'color: red; background-color: black;', 'color: green;', 'color: yellow;', 'color: blue;', 'color: black; background: pink;'];
let lastRangeEnd = 0;
const styleEl = document.createElement('style');
for (let i = 0; i < rangeEnds.length; i++) {
for (let j = lastRangeEnd + 1; j < rangeEnds[i]; j++) {
styleEl.innerHTML += '.class-' + j + ',';
}
styleEl.innerHTML += '.class-' + rangeEnds[i] + '{' + styles[i] + '}';
lastRangeEnd = rangeEnds[i];
}
document.querySelector('head').append(styleEl);
<!doctype html>
<html>
<head>
<title>Classes</title>
</head>
<body>
<div class="class-1">ABCD</div>
<div class="class-19">ABCD</div>
<div class="class-21">ABCD</div>
<div class="class-40">ABCD</div>
<div class="class-41">ABCD</div>
<div class="class-48">ABCD</div>
<div class="class-100">ABCD</div>
</body>
If all elements will have the same property which is {color:pink}
You can create only one class (lets call it .pink)
.pink {
color: pink;
}
and then you can simply give your elements the .pink class.
One of class attribute's main purpose is to define a shared style reference name. It is rather not a very good practice to want to reference multiple class references and let them share the same styling.
The best way to get around this is to have a common class attribute name YourClassName. This way, any element you want the styling applied to can have that class appended to its class attribute through element.classList.add(YourClassName) with JS. And, that would solve all the hussle of having to worry about putting multiple classe names and I cannot think of any 1 situation that would force you to declare each element class separated by commas provided that they are to receive the same styling.
The OP asks if it’s possible to have a ‘number range’ (array) at the end of CSS classes that shares the same name, but ending on 1, 2, 3, etc.
As #zer00ne pointed out; You can target multiple classes with one "class". When defining your class selector - leave out the numbers, but make the class name unique.
So, if the class names are i.e. my-row-class-1, my-row-class-2, etc., write the selector like this;
[class^="my-row-class-"] {
color: pink;
}
Pro tip: Instead of using class^= selector, it's possible to do this for id^= as well - and more. Check out the MDN web docs for more info.
I'm generating a table using xslt, but for this question I'll keep that side out of it, as it relates more to the actual generated structure of a html table.
What I do is make a vertical table as follows, which suits the layout needed for the data concerned that originated in a spreadsheet. Example is contrived for brevity, actual data fields contain lengthy strings and many more fields.
Title: something or rather bla bla
Description: very long desription
Field1: asdfasdfasdfsdfsd
Field2: asdfasfasdfasdfsdfjasdlfksdjaflk
Title: another title
Description: another description
Field1:
Field2: my previous field was blank but this one is not, anyways
etc.
The only way so far I found to generate such a html table is using repeating tags for every field and every record e.g.:
<tr><th>Title</th><td>something or rather bla bla</td></tr>
<tr><th>Description</th><td>very long desription</td></tr>
...
<tr><th>Title</th><td>another title</td></tr>
<tr><th>Description</th><td>another description</td></tr>
...
Of course this is semantically incorrect but produces correct visual layout. I need it to be semantically correct html, as that's the only sane way of later attaching a filtering javascript facility.
The following correct semantically produces an extremely wide table with a single set of field headers on the left:
<tr><th>Title</th><td>something or rather bla bla</td><td>another title</td></tr>
<tr><th>Description</th><td>very long desription</td><td>another description</td></tr>
...
So to summarise, need a html table (or other html structure) where it's one record under another (visually) with repeating field headers, but the field headers must not be repeated in actual code because that would wreck any record based filtering to be added later on.
Yo. Thanks for updating your question, and including some code. Typically you'd also post what you've tried to correct this issue - but I'm satisfied enough with this post.
Since you want the repeating headers in vertical layout (not something I've seen often, but I can understand the desire), you don't have to modify the HTML formatting, just use a bit more JavaScript to figure it out. I haven't gone through and checked to see if I'm doing things efficiently (I'm probably not, since there are so many loops), but in my testing the following can attach to a vertical table and filter using a couple variables to indicate how many rows there are in each entry.
Firstly, here's the HTML I'm testing this one with. Notice I have a div with the id of filters, and each of my filter inputs has a custom attribute named filter that matches the header of the rows they are supposed to filter:
<div id='filters'>
Title: <input filter='Title'><br>
Desc: <input filter='Description'>
</div>
<table>
<tr><th>Title</th><td>abcd</td></tr>
<tr><th>Description</th><td>efgh</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mdep</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
<tr><th>Title</th><td>ijkl</td></tr>
<tr><th>Description</th><td>mnop</td></tr>
</table>
Here are the variables I use at the start:
var filterTable = $('table');
var rowsPerEntry = 2;
var totalEntries = filterTable.find('tbody tr').size() / rowsPerEntry;
var currentEntryNumber = 1;
var currentRowInEntry = 0;
And this little loop will add a class for each entry (based on the rowsPerEntry as seen above) to group the rows together (this way all rows for an entry can be selected together with a class selector in jQuery):
filterTable.find('tbody tr').each(function(){
$(this).addClass('entry' + currentEntryNumber);
currentRowInEntry += 1;
if(currentRowInEntry == rowsPerEntry){
currentRowInEntry = 0;
currentEntryNumber += 1;
}
});
And the magic; on keyup for the filters run a loop through the total number of entries, then a nested loop through the filters to determine if that entry does not match either filter's input. If either field for the entry does not match the corresponding filter value, then we add the entry number to our hide array and move along. Once we've determined which entries should be hidden, we can show all of the entries, and hide the specific ones that should be hidden:
$('#filters input').keyup(function(){
var hide = [];
for(var i = 0; i < totalEntries; i++){
var entryNumber = i + 1;
if($.inArray(entryNumber, hide) == -1){
$('#filters input').each(function(){
var val = $(this).val().toLowerCase();
var fHeader = $(this).attr('filter');
var fRow = $('.entry' + entryNumber + ' th:contains(' + fHeader + ')').closest('tr');
if(fRow.find('td').text().toLowerCase().indexOf(val) == -1){
hide.push(entryNumber);
return false;
}
});
}
}
filterTable.find('tbody tr').show();
$.each(hide, function(k, v){
filterTable.find('.entry' + v).hide();
});
});
It's no masterpiece, but I hope it'll get you started down the right path.
Here's a fiddle too: https://jsfiddle.net/bzjyfejc/
Most of us know that now and then some tags get a deprecated status, which means that it has been outdated. Either it is followed by a newer HTML construct, or it can be done in CSS (take for example <center>). The question that I'm wondering about, though, is: when a tag or element gets deprecated will it be removed from browser support in the future? In other words, currently all browsers that I know of support <center>, but I can imagine that it might not be efficient for browsers to keep supporting deprecated content. Therefore, support must drop after some time.
Is it likely that browsers drop support for a tag or element that once was quite common? To provide a question that's better suited for the SO question-answer template, I'll rephrase all of the above: are cases known where browsers have dropped support for a property or element that once was common?
The only thing that I could find was in the docs, stating:
Deprecated A deprecated element or attribute is one that has been outdated by newer constructs. Deprecated elements are defined in the
reference manual in appropriate locations, but are clearly marked as
deprecated. Deprecated elements may become obsolete in future versions
of HTML.
User agents should continue to support deprecated elements for reasons of backward compatibility.
Definitions of elements and attributes clearly indicate which are
deprecated.
As I see it, this is not opinion based. I am asking if there are cases known of tags that are actually not being supported by browsers any more. That's not bound by opinion. However I do understand that this question has quite an open feel to it. Therefore I'd like to clarify that I am looking for actual and factual evidence of browsers dropping support. I'm not asking about any foreseers to come forward and confess their magical powers, I'm merely looking for examples from cases that have occurred in the past.
The code below creates elements from deprecated tags, and it outputs what the browser thinks the newly-created elements really are:
var dep = 'acronym|applet|basefont|bgsound|big|blink|center|dir|font|frame|frameset|hgroup|isindex|listing|marquee|menu|multicol|nextid|nobr|noembed|noframes|plaintext|s|spacer|strike|tt|u|xmp'.split('|'),
s = '<table>',
els = [];
dep.forEach(function(val) {
var el = document.createElement(val),
str = el.toString().slice(8, -1),
style = 'HTMLElement HTMLPhraseElement HTMLBlockElement HTMLPreElement HTMLSpanElement HTMLDivElement'.indexOf(str) > -1 ? 'background:yellow' :
str === 'HTMLUnknownElement' ? 'background:orange' :
'';
el.innerHTML = val;
els.push(el);
s += '<tr style="' + style + '">' +
'<td>' + val +
'<td>' + str +
'<td>';
});
s += '</table>';
document.getElementById('list').innerHTML = s;
var td = document.querySelectorAll('td:last-child');
dep.forEach(function(val, idx) {
td[idx].appendChild(els[idx]);
});
table {
font: 12px verdana;
border-spacing: 0px;
border: 1px solid black;
}
td {
border-right: 1px solid #ddd;
border-bottom: 1px solid #bbb;
}
<div id="list"></div>
We can assume that anything highlighted in orange is not supported by that browser, anything highlighted in yellow is iffy, and the rest should be completely supported.
To determine the degree of "iffyness" of the generic "HTMLElements," we could compare their default CSS styles to the default styles of a span or div element. The Snippet below does this by adding a new column to the listing, which shows styles distinct to each deprecated element.
Elements of type "HTMLUnknownElement" have no distinct styles (as expected). Most other elements do. For those that don't, that doesn't necessarily mean they don't support distinct attributes. For example, the font element's styles match the default styles of a span – but the font element supports attributes size and face, which the span does not support.
function getStyles(el) {
var gcs= getComputedStyle(el),
st= gcs.cssText ? gcs.cssText.split(/; */) : el.currentStyle,
obj= {},
i, j, sp;
for(var i = 0 ; i < st.length ; i++) {
sp= st[i].split(':')[0];
if(j = gcs.getPropertyValue(sp)) {
obj[sp]= j;
}
}
return obj;
} //getStyles
function compStyles(st1, st2) {
var s= '';
for(var i in st1) {
if(st1[i] && st1[i] !== st2[i]) {
s+= i+': '+st1[i]+' - '+st2[i]+'; ';
}
}
return s;
} //compStyles
var dep= 'acronym|applet|basefont|bgsound|big|blink|center|dir|font|frame|frameset|hgroup|isindex|listing|marquee|menu|multicol|nextid|nobr|noembed|noframes|plaintext|spacer|strike|tt|xmp'.split('|'),
s= '<table>',
els= [],
spanStyles=
getStyles(
document.body.appendChild(
document.createElement('span')
)
),
divStyles=
getStyles(
document.body.appendChild(
document.createElement('div')
)
);
dep.forEach(function(val) {
var el= document.createElement(val),
str= el.toString().slice(8,-1),
display,
style= 'HTMLElement HTMLPhraseElement HTMLBlockElement HTMLPreElement HTMLSpanElement HTMLDivElement'.indexOf(str)>-1 ? 'background:yellow' :
str==='HTMLUnknownElement' ? 'background:orange' :
'';
document.body.appendChild(el);
display= getStyles(el).display;
el.innerHTML= val;
els.push(el);
s+= '<tr style="'+style+'">'+
'<td>'+val+
'<td>'+str+
'<td>'+display+
'<td>'+compStyles(
getStyles(el),
display==='block' ? divStyles : spanStyles
)+
'<td>';
});
s+= '</table>';
document.getElementById('list').innerHTML= s;
var td= document.querySelectorAll('td:last-child');
dep.forEach(function(val, idx) {
td[idx].appendChild(els[idx]);
});
table {
font: 12px verdana;
border-spacing: 0px;
border: 1px solid black;
}
td {
vertical-align: top;
border-right: 1px solid #ddd;
border-bottom: 1px solid #bbb;
}
<div id="list"></div>
It has happened before.
The <blink> HTML tag (see wiki and docs) used to be quite common, but it was considered very user-unfriendly and therefore became deprecated. Netscape, Opera and also Firefox used to support it. Firefox was the last to finally completely remove it in version 23.
The <blink> element was exceptionally obtrusive and became very unpopular, so the drop in support was no surprise... but it is also a question of backwards compatibility. Do the benefits of removing something outweigh the loss of its functionality? <blink> could be removed without much repercussion (things would just stop blinking). On the other hand, a tag like <marquee> (which has also received a lot of negative press) is still supported, most likely because removing it may effect content directly.
All in all I think that the issue isn't really if existing browsers will remove deprecated css/html (since it is a relatively rare occurrence), but rather whether new/future browsers will support them. Backwards compatibility will only go so far.
To sum up: Yes, so don't use deprecated features.
I have an application that is having issue when populating selects with over 100 items. This problem only occurs in IE8. I am using angularjs to do the population, but my research shows that this is a general problem with IE8. What solutions have others used to deal with this problem. We have over 40,000 users tied to IE8 for the foreseeable future (Fortune 200 company) so moving to another browser is not an option.
Some thoughts I had.
Create a series of option tags as a one long string in memory and replace the innerHTML of the . But running some people samples this does not appear to solve the issue.
Originally populating the select with a few and then adding the rest as the user scrolls down. I am not sure if this is possible, or how to implement this
I am sure others have run into this issue. Does anyone have some ideas?
Thanks,
Jerry
Another solution that preserves the original <select> is to set the <option> values after adding the options to the <select>.
Theory
Add the <option> elements to a document fragment.
Add the document fragment to the <select>.
Set the value for each <option>.
Practice
In practice we end up with a couple issues we have to work around to get this to work:
IE11 is very slow when setting the value for each individual <option>.
IE8 has selection bugs because it isn't properly doing a re-flow/layout on the <select>.
Result
To handle these what we really do is something like the following:
Add the <option> tags to a document fragment. Make sure to set the values so that step 3 is a no-op in IE11.
Add the document fragment to the <select>.
Set the value for each <option>. In IE8 this will set the values, in IE11 this is a no-op.
In a setTimeout add and remove a dummy <option>. This forces a re-flow.
Code
function setSelectOptions(select, options)
{
select.innerHTML = ''; // Blank the list.
// 1. Add the options to a document fragment.
var docFrag = document.createDocumentFragement();
for (var i = 0; i < options.length; i++)
{
var opt = document.createElement('option');
opt.text = options[i];
docFrag.appendChild(opt);
}
// 2. Add the document fragment to the select.
select.appendChild(docFrag);
// 3. Set the option values for IE8. This is a no-op in IE11.
for (i = 0; i < options.length; i++)
select.options[i].text = options[i];
// 4. Force a re-flow/layout to fix IE8 selection bugs.
window.setTimeout(function()
{
select.add(document.createElement('option'));
select.remove(select.options.length - 1);
}, 0);
}
The best solution seems to be to create the Select and it's options as a text string and add that string as the innerHTML of the containing tag such as a DIV. Below is some code.
<div id="selectHome" ></div>
In JS (from angular controller)
function insertSelect(divForSelect) {
var str = "<select id='myselect'>";
for (var i = 0; i < data.length; i++) {
str += '<option>' + data[i] + '</data>';
}
str += '</select>';
divForSelect.innnerHTML = str;
}
Note that inserting options into a existing Select is very slow (8,000 msecs for 2000 items). But, if the select and the options are inserted as a single string it is very fast (12 msec for 2000 items).
I have a well formatted HTML table (the HTML text is generated by a reporting tool) and I want to turn it into a "programmable" table in GWT.
I need to show that content as it is provided, but I also need to inspect the DOM, get the tables, and add ClickHandler's to the rows and cells.
I am able to do something similar with images:
Html html = new HTML(htmlText);
ImageElement domElement = getChildImageByTagAndId(html.getElement(), "img", "blah");
Image image = Image.wrap(domElement);
image.addClickHandler(...);
My question is: What is the correct way to do this with tables?
I could not find a wrap() method for <table>, <tr>, and <td> elements.
Note: this question was asked (and not answered) in the comments in the accepted answer here.
I am unaware of an easy way to wrap an existing Table with the sub tr and td elements.
Given the constraint that you are relying on a third party tool, I recommend trying to build a custom table parser which will parse the existing table, build a FlexTable, and then replace the existing table with the new FlexTable.
As ugly as this is, I am not sure there is a better way...
It may depend on how exactly the table is setup, but you may try playing around with something like the below example to go through the existing table and use the getNodeValue() to build the content of the FlexTable....
Element table = DOM.getElementById("someTableId");
int numTopNodes = table.getChildNodes().getLength();
for(int topNode = 0; topNode < numTopNodes; topNode++){
Node top = table.getChildNodes().getItem(topNode);
System.out.println("Top Node: "+top);
for(int subNode = 0; subNode < top.getChildCount(); subNode ++){
Node sub = top.getChildNodes().getItem(subNode);
System.out.println("Sub Node: "+sub);
for(int rows = 0; rows < sub.getChildCount(); rows ++){
Node row = sub.getChildNodes().getItem(rows);
System.out.println("Row: "+row);
for(int cells = 0; cells < row.getChildCount(); cells++ ){
Node cell = row.getChildNodes().getItem(cells);
System.out.println("CELL: "+cell.getNodeValue()); // use value to build to build a new FlexTable
}
}
}
}