diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..7e45dcf --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: marcj diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..4f29079 --- /dev/null +++ b/.npmignore @@ -0,0 +1,2 @@ +.idea +tests diff --git a/README.md b/README.md index 833190c..61c88f5 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,10 @@ Features: - no performance issues since it listens only on size changes of elements that have element query rules defined through css. Other element query polifills only listen on `window.onresize` which causes performance issues and allows only to detect changes via window.resize event and not inside layout changes like css3 animation, :hover, DOM changes etc. - no interval/timeout detection. Truly event-based through integrated ResizeSensor class. + - automatically discovers new DOM elements. No need to call javascript manually. - no CSS modifications. Valid CSS Syntax - - all CSS selectors available. Uses regular attribute selector. No need to write rules in HTML. - - supports and tested in webkit, gecko and IE(7/8/9/10/11) + - all CSS selectors available. Uses regular attribute selector. No need to write rules in HTML/JS. + - supports and tested in webkit, gecko and IE(10+) - `min-width`, `min-height`, `max-width` and `max-height` are supported so far - works with any layout modifications: HTML (innerHTML etc), inline styles, DOM mutation, CSS3 transitions, fluid layout changes (also percent changes), pseudo classes (:hover etc.), window resizes and more - no Javascript-Framework dependency (works with jQuery, Mootools, etc.) @@ -50,7 +51,7 @@ More demos and information: http://marcj.github.io/css-element-queries/ As you can see we use the `~=` [attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors). Since this css-element-queries polyfill adds new element attributes on the DOM element -(`
`) depending on your actual CSS, +(`
`) depending on your actual CSS and element's dimension, you should always use this attribute selector (especially if you have several element query rules on the same element). ```html @@ -88,15 +89,15 @@ Here live http://marcj.github.io/css-element-queries/. If you're using a module loader you need to trigger the event listening or initialization yourself: ```javascript -var EQ = require('node_modules/css-element-queries/ElementQueries'); +var ElementQueries = require('css-element-queries/src/ElementQueries'); //attaches to DOMLoadContent -EQ.listen(); +ElementQueries.listen(); //or if you want to trigger it yourself. // Parse all available CSS and attach ResizeSensor to those elements which have rules attached // (make sure this is called after 'load' event, because CSS files are not ready when domReady is fired. -EQ.init(); +ElementQueries.init(); ``` ## Issues @@ -104,6 +105,7 @@ EQ.init(); - So far does not work on `img` and other elements that can't contain other elements. Wrapping with a `div` works fine though (See demo). - Adds additional hidden elements into selected target element and forces target element to be relative or absolute. - Local stylesheets do not work (using `file://` protocol). + - If you have rules on an element that has a css animation, also add `element-queries`. E.g. `.widget-name { animation: 2sec my-animation, 1s element-queries;}`. We use this to detect new added DOM elements automatically. ## License diff --git a/css-element-queries.d.ts b/css-element-queries.d.ts new file mode 100644 index 0000000..3fe0706 --- /dev/null +++ b/css-element-queries.d.ts @@ -0,0 +1,2 @@ +export { ResizeSensor, ResizeSensorCallback, Size } from "./src/ResizeSensor"; +export { ElementQueries } from './src/ElementQueries'; \ No newline at end of file diff --git a/package.json b/package.json index 4ae45dc..964230f 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "css-element-queries", - "version": "0.3.2", - "description": "CSS-Element-Queries Polyfill. proof-of-concept for high-speed element dimension/media queries in valid css.", + "version": "1.2.3", + "description": "CSS-Element-Queries Polyfill. Proof-of-concept for high-speed element dimension/media queries in valid css.", "main": "index.js", + "typings": "css-element-queries.d.ts", "directories": { "test": "test" }, diff --git a/src/ElementQueries.d.ts b/src/ElementQueries.d.ts new file mode 100644 index 0000000..00c5d93 --- /dev/null +++ b/src/ElementQueries.d.ts @@ -0,0 +1,14 @@ +export declare class ElementQueries { + /** + * Attaches to DOMLoadContent + */ + static listen(): void; + + /** + * Parses all available CSS and attach ResizeSensor to those elements which have rules attached. + * Make sure this is called after 'load' event, because CSS files are not ready when domReady is fired. + */ + static init(): void; +} + +export default ElementQueries; diff --git a/src/ElementQueries.js b/src/ElementQueries.js index ab121cb..4fe4298 100755 --- a/src/ElementQueries.js +++ b/src/ElementQueries.js @@ -1,9 +1,10 @@ +'use strict'; + /** * Copyright Marc J. Schmidt. See the LICENSE file at the top-level * directory of this distribution and at * https://github.com/marcj/css-element-queries/blob/master/LICENSE. */ -; (function (root, factory) { if (typeof define === "function" && define.amd) { define(['./ResizeSensor.js'], factory); @@ -20,10 +21,15 @@ * @type {Function} * @constructor */ - var ElementQueries = function() { + var ElementQueries = function () { + // + + + + + +
+

+

Examples +
+ Drag the gray line at the right to see it in action. +
+

+
+
+
+

Responsive Text

+
+
+
+

Element responsiveness FTW!

+
+ + + + +
+

Element detachable

+
+ + + +
+

Dynamic element {{id}}

+
+ + +
+
+
+ +
+
+ +
+
+
+
+

Responsive Image

+
+
+
+ + + +
+ + The image above has a default of 700px. Shrink or expend the container too see responsive image + working. + Thanks @placehold.it +
+
+
+ +
+
+
+
+

Responsive Widget

+
+
+

Demo 1

+ This is content from the first responsive demo without media queries. It uses the element + queries + provided by this library. +
+
+ +
+ +
+
+ +
+
+
+ +
+

Responsive Layout

+
+
+

Demo 2

+
+ Box +
+
+ First 1/2 box +
+
+ Second 1/2 box +
+
+
+
+
+ +
+
+ +
+
+
+
+

Responsive Animation

+
+
+

Demo 3 - width + +

+
+ This box is animated through css transitions. + We attached a resize-listener to this box. See below. +
+
+ No changes. +
+
+
+
+
+

Demo 4 - height + +

+
+ This box is animated through css transitions. + We attached a resize-listener to this box. See below. +
+
+ No changes. +
+
+
+
+ +
+

ResizeSensor Demo

+
+
+
+ 0 changes +
+
+
+
+ CSS-Element-Queries comes with a Javascript ResizeSensor class you can use in Javascript directly. +
+ +
+
+
+ +
+

Invisible Demo

+
+
+ Press button to show. +
+ This should be red. +
+
+
+
+ + +
+
+ +
+

Performance Demo

+
+
+ Performance Test: + + + + + + + +
+
+
+
+
+
+
+
+ diff --git a/tests/demo.js b/tests/demo.js new file mode 100644 index 0000000..e4899a1 --- /dev/null +++ b/tests/demo.js @@ -0,0 +1,135 @@ +$(document).ready(function () { + console.log('ready'); + + // $("textarea.html").each(function(idx, textarea) { + // CodeMirror.fromTextArea(textarea, { + // lineNumbers: true, + // mode: "htmlmixed", + // readOnly: true + // }); + // }); + // + // $("textarea.css").each(function(idx, textarea) { + // CodeMirror.fromTextArea(textarea, { + // lineNumbers: true, + // mode: "css", + // readOnly: true + // }); + // }); + // + // $("textarea.javascript").each(function(idx, textarea) { + // CodeMirror.fromTextArea(textarea, { + // lineNumbers: true, + // mode: "javascript", + // readOnly: true + // }); + // }); +}); + +function ResizerDemo(element) { + element = $(element); + var handler = $('
'); + var info = $('
'); + + element.append(handler); + element.append(info); + + var hammer = new Hammer(element[0], {recognizers: [ + [Hammer.Pan, { threshold: 0}] + ]}); + + var startWidth; + element.on('mousedown', function(e){ + e.preventDefault(); + }); + hammer.on('panstart', function(e) { + startWidth = element[0].clientWidth; + }); + + hammer.on('panmove', function(e) { + element[0].style.width = (startWidth + e.deltaX) + 'px'; + info.html(element[0].clientWidth + 'px x ' + element[0].clientHeight + 'px'); + }) +} + +$( document ).ready(function(){ + $('.examplesResizerDemos').each(function(idx, element){ + new ResizerDemo(element); + }); + + perfTest(); + example3(); + example4(); + example5(); +}); + +function perfTest(){ + var container = $('#dynamicContainer'); + var dynamicCount = $('#dynamicCount'); + var dynamicCounter = $('#dynamicCounter'); + + window.detachDynamic = function() { + container.children().each(function(idx, element) { + ResizeSensor.detach(element); + }); + }; + + window.removeDynamic = function() { + container.html(''); + }; + + window.addDynamic = function() { + container.html(''); + var i = 0, to = dynamicCount.val(), div, counter = 0; + for (; i < to; i++) { + div = $('
#'+i+'
'); + container.append(div); + + new ResizeSensor(div, function(){ + counter++; + dynamicCounter.html(counter + ' changes.'); + }); + } + } +} + +function example3(){ + var logger = $('#example-3-log'); + var box = $('#example-3-box'); + + $('#startStop3').on('click', function(){ + if (box.hasClass('example-3-box-start')) { + box.removeClass('example-3-box-start'); + } else { + box.addClass('example-3-box-start'); + } + }); + new ResizeSensor(box, function(el){ + logger.html('Changed to ' + box[0].clientWidth+'px width.'); + }); + +} + +function example4(){ + var logger = $('#example-4-log'); + var box = $('#example-4-box'); + + $('#startStop4').on('click', function(){ + if (box.hasClass('example-4-box-start')) { + box.removeClass('example-4-box-start'); + } else { + box.addClass('example-4-box-start'); + } + }); + new ResizeSensor(box, function(){ + logger.html('Changed to ' + box[0].clientHeight+'px height.'); + }); +} + +function example5(){ + var box = $('#example-5'); + var changed = 0; + new ResizeSensor(box.parent(), function(){ + box[0].innerHTML = (++changed) + ' changes. ' + box.parent()[0].clientWidth+'px/'+box.parent()[0].clientHeight+'px'; + }); +} \ No newline at end of file diff --git a/tests/late-trigger.html b/tests/late-trigger.html new file mode 100644 index 0000000..71a2b17 --- /dev/null +++ b/tests/late-trigger.html @@ -0,0 +1,48 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/mutation/app.js b/tests/mutation/app.js new file mode 100644 index 0000000..2100612 --- /dev/null +++ b/tests/mutation/app.js @@ -0,0 +1,75 @@ +var __values = (this && this.__values) || function (o) { + var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; + if (m) return m.call(o); + return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; +}; +var e_1, _a, e_2, _b; +var state = { + dragged: null +}; +var i = 0; +try { + for (var _c = __values(document.getElementsByClassName('drag')), _d = _c.next(); !_d.done; _d = _c.next()) { + var item = _d.value; + i++; + item.setAttribute('draggable', 'true'); + item.setAttribute('id', 'drag-' + i); + (function (element) { + var title = 'Drag me #' + i; + element.setAttribute('data-label', title); + new ResizeSensor(element, function (size) { + element.setAttribute('data-label', title + " (" + size.width + "x" + size.height + ")"); + }); + })(item); + item.addEventListener('dragstart', function (event) { + state.dragged = event.target; + event.dataTransfer.setData('text', 'thanks firefox'); + event.dataTransfer.dropEffect = 'move'; + }); + } +} +catch (e_1_1) { e_1 = { error: e_1_1 }; } +finally { + try { + if (_d && !_d.done && (_a = _c.return)) _a.call(_c); + } + finally { if (e_1) throw e_1.error; } +} +var _loop_1 = function (item) { + (function (element) { + item.addEventListener('drop', function (event) { + event.preventDefault(); + item.classList.remove('drag-hover'); + state.dragged.parentNode.removeChild(state.dragged); + element.appendChild(state.dragged); + state.dragged = null; + }); + })(item); + item.addEventListener('dragleave', function (event) { + item.classList.remove('drag-hover'); + }); + item.addEventListener('dragover', function (event) { + item.classList.add('drag-hover'); + }); + item.addEventListener('dragover', function (event) { + event.preventDefault(); + }); +}; +try { + for (var _e = __values(document.getElementsByClassName('container')), _f = _e.next(); !_f.done; _f = _e.next()) { + var item = _f.value; + _loop_1(item); + } +} +catch (e_2_1) { e_2 = { error: e_2_1 }; } +finally { + try { + if (_f && !_f.done && (_b = _e.return)) _b.call(_e); + } + finally { if (e_2) throw e_2.error; } +} diff --git a/tests/mutation/app.ts b/tests/mutation/app.ts new file mode 100644 index 0000000..15974f7 --- /dev/null +++ b/tests/mutation/app.ts @@ -0,0 +1,56 @@ +declare const ResizeSensor; + +const state: { + dragged: Element +} = { + dragged: null +}; + +let i = 0; + +for (const item of document.getElementsByClassName('drag')) { + i++; + item.setAttribute('draggable', 'true'); + item.setAttribute('id', 'drag-' + i); + + (element => { + const title = 'Drag me #' + i; + element.setAttribute('data-label', title); + + new ResizeSensor(element, (size) => { + element.setAttribute('data-label', `${title} (${size.width}x${size.height})`); + }); + })(item); + + item.addEventListener('dragstart', (event: DragEvent) => { + state.dragged = event.target; + event.dataTransfer.setData('text', 'thanks firefox'); + event.dataTransfer.dropEffect = 'move'; + }); +} + +for (const item of document.getElementsByClassName('container')) { + (element => { + item.addEventListener('drop', (event) => { + event.preventDefault(); + item.classList.remove('drag-hover'); + + state.dragged.parentNode.removeChild(state.dragged); + element.appendChild(state.dragged); + + state.dragged = null; + }); + })(item); + + item.addEventListener('dragleave', (event) => { + item.classList.remove('drag-hover'); + }); + + item.addEventListener('dragover', (event) => { + item.classList.add('drag-hover'); + }); + + item.addEventListener('dragover', (event) => { + event.preventDefault(); + }); +} diff --git a/tests/mutation/index.html b/tests/mutation/index.html new file mode 100644 index 0000000..6d2ddce --- /dev/null +++ b/tests/mutation/index.html @@ -0,0 +1,76 @@ + + + + + + + + +
+
+
+
+ +
+
+
+
+ + + \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7b56f0e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "es5", + "downlevelIteration": true, + "lib" : ["dom","es6","dom.iterable","scripthost", "es2015.iterable", "es2015.collection"] + } +} \ No newline at end of file